RELEASE_NOTES.txt0100664 0000000 0000000 00000407307 14443065100 012410 0ustar000000000 0000000 Release 5.2.2 ------------------ This is a maintenance release that corrects several defects discovered since release 5.2.1 including a major defect that can cause HTTP/2 connections allocate excessive amount of memory for their output frame buffer if the opposite endpoint transmits a high value of MAX_FRAME_SIZE in its settings. Change Log ------------------- * HTTPCORE-752: I/O reactor fails to initialize socket timeout for TLS connections correctly resulting in infinite (no timeout) by default. Contributed by Oleg Kalnichevski * HTTPCORE-751: H2 protocol handler always resizes the output frame buffer to the remove MAX_FRAME_SIZE instead of doing so only then the remote MAX_FRAME_SIZE is lesser than the current MAX_FRAME_SIZE (partially reverts HTTPCORE-707). Contributed by Oleg Kalnichevski * HTTPCORE-750: Fixed a defect causing AbstractIOSessionPool to create multiple connections under high load at initialization time due to a race condition. Contributed by Oleg Kalnichevski * Handle UnsupportedOperationException in getApplicationProtocol. Contributed by Arturo Bernal * HTTPCORE-742: BasicHttpRequest#setUri does not correctly reset internal state. Contributed by Oleg Kalnichevski * HTTPCORE-733: BasicAsyncEntityProducer sends an extra trailing 0 with UTF-8 encoded content Contributed by Oleg Kalnichevski * Do not duplicate the HttpMessage instance variable slot in subclasses of AbstractMessageWrapper. Contributed by Gary Gregory Release 5.2.1 ------------------ This is a maintenance release that corrects several minor defects discovered since release 5.2 and fixes SOCKS proxy protocol support in the async transport. Change Log ------------------- * HTTPCLIENT-2248: Fixed broken TLS over SOCKS. Contributed by Oleg Kalnichevski * I/O reactor connect process redesign. Contributed by Oleg Kalnichevski * SOCKS protocol handling: Delegate resolution of unknown hostnames to the SOCKS proxy. Contributed by Oleg Kalnichevski * I/O reactor to validate remote endpoint address at the last moment immediately before connect. Contributed by Oleg Kalnichevski * Bug fix: I/O reactor must handle all runtime exceptions when connecting to remote endpoints. Contributed by Oleg Kalnichevski * Added org.apache.hc.core5.http.protocol.HttpCoreContext#toString(). Contributed by Gary Gregory * Examples should log exceptions instead of swallowing them. Contributed by Gary Gregory * HTTPCORE-729: NPE in ServerHttp1IOEventHandlerFactory due to a missing null check Contributed by Oleg Kalnichevski Release 5.2 ------------------ This is the first GA release in the 5.2 release series. This release finalizes the 5.2 APIs and also corrects a number of defects discovered since the previous release. Please note that 5.2 upgrades the minimal JRE level to version 8 (8u251 is required). Notable changes and features included in the 5.2 series: * Upgrade to Java 8. * Improved support for TLS upgrade and HTTP protocol upgrade (async). * Improved HTTP protocol negotiation. * Improved customization of connection listeners (async). * Improved parsing and formatting of URI components. * Use of Java 8 date / time APIs Change Log ------------------- * Bug fix: HTTP/1.1 protocol handler must continue message processing as long as there is data in the session input buffer. Contributed by Oleg Kalnichevski * HTTPCORE-726: Improved capacity management in SharedInputBuffer. Contributed by John Leacox * Bug fix: Fixed incorrect calculation of the capacity increment in StringAsyncEntityConsumer. Contributed by Oleg Kalnichevski * Add and use TLS accessor methods to use lambdas from httpclient5 later. Contributed by Gary Gregory * Deprecate org.apache.hc.core5.util.LangUtils.equals(Object, Object) in favor or java.util.Objects.equals(Object, Object). Deprecate org.apache.hc.core5.util.LangUtils.equals(Object[], Object[]) in favor or java.util.Arrays.equals(Object[], Object[]). Contributed by Gary Gregory * URI Builder performance optimization (#355) Contributed by jkmcl Release 5.2 BETA2 ------------------ This BETA release corrects a major regression in the TLS handshake handling code introduced in the previous BETA release. This release also includes all fixes from the 5.1 (stable) branch. Please note that 5.2 upgrades the minimal JRE level to version 8 (8u251 is required). Change Log ------------------- * HTTPCORE-713: Optimize InetAddressUtils#isIPv6*Address; check input colon count before performing IPv6 regex validation. Contributed by David Schlosnagle * HTTPCORE-710: In case of some TLS handshake failures (protocol version mismatch) the local TLS engine quietly closes the stream instead of throwing a handshake exception. Contributed by Oleg Kalnichevski * Corrected TLS upgrade support in HttpAsyncRequester. Contributed by Oleg Kalnichevski * Bug fix: Non-blocking TLS sessions fail to update their event interest mask upon TLS handshake initiation. Contributed by Oleg Kalnichevski * Upgrade to RxJava3 Contributed by Ryan Schmitt Release 5.2 BETA1 ------------------ This is the first BETA release in the 5.2 release series that marks the completion of major API changes and starts the transition toward a GA phase. This release also includes all fixes from the 5.1 (stable) branch. Change Log ------------------- * Improved HTTP protocol negotiation. Contributed by Oleg Kalnichevski * Added #clone to BasicHeader. Contributed by Arturo Bernal * Use subclass of ConnectionClosedException to signal request execution failures due to the connection being closed. Requests failed with this exception should generally be safe to re-execute. Contributed by Oleg Kalnichevski * HTTPCORE-698: Migratation to Unit 5. Contributed by Arturo Bernal * HTTPCORE-697: Replaced SimpleDateFormat with Java 8 Time APIs. Contributed by Arturo Bernal * Fixed #format in Deadline; improved #hashCode; replaced SimpleDatteFormat with Java 8 Time APIs. Contributed by Oleg Kalnichevski * Singleton INSTANCE added to RequestConnControl, RequestContent, RequestData, RequestTargetHost and RequestUserAgent. Contributed by Arturo Bernal * HTTPCORE-692: added new rules for H2 header check as per rfc7540 section 8.1.2.2 and 8.1.2.3. Contributed by 风起 * HTTPCORE-691: HttpService and HttpRequestExecutor builders. Contributed by Arturo Bernal * Added URIAuthority and NamedEndpoint setters to URIBuilder (#308). Contributed by Gary Gregory Release 5.2 ALPHA2 ------------------ This is the second ALPHA release in the 5.2 release series that fixes a regression in the TLS layer introduced by the previous ALPHA and adds a number of incremental improvements. Change Log ------------------- * Add PathEntityProducer, an NIO entity provider (#302). Contributed by Gary Gregory * Add SSLContextBuilder NIO Path versions of IO File APIs and re-implement internals with NIO (#301). Contributed by Gary Gregory * Allow setting parameters to null arrays and lists to behave like empty (#300). Contributed by Gary Gregory * Bug fix, regression: TLS handshake result callback does not get called in case of a timeout. Contributed by Oleg Kalnichevski * HTTPCLIENT-2174: URUBuilder to return a new empty list instead of unmodifiable Collections#emptyList. Contributed by Oleg Kalnichevski Release 5.2 ALPHA1 ------------------ This is the first ALPHA release in the 5.2 release series that upgrades minimal JRE level to version 1.8 (8u251 is required) and includes several protocol level and API improvements. It also includes all bug fixes from the 5.1 branch. Change Log ------------------- * Improved Travis CI build Performance. Contributed by Chen Zhang <340355960 at qq.com> * HTTPCORE-682: Custom provider for key manager/trust manager initialization (#296) Contributed by Pawel Veselov * Bug fix: fix data race in StrictConnPool. Contributed by Carter Kozak * Added utility method to add name value pair for uri builder (#288). Contributed by Anurag Agarwal * Use jep244 (ALPN support) back-ported to java 8u251. Contributed by Oleg Kalnichevski * Bug fix: Don't change conn flow control window based on remote SETTINGS. Contributed by Ryan Schmitt * HTTPCLIENT-1916: Add URIBuilder.removeParameter (#283). Contributed by Peter Dettman * Improved parsing and formatting of URI components. Contributed by Oleg Kalnichevski * Let TimeValue/Timeout convert to and from Duration (#263). Contributed by Gary Gregory * Add URIBuilder#getFirstQueryParam(String) to query a parameter by name (#264). Contributed by Gary Gregory * Protocol upgrade APIs redesign. Contributed by Oleg Kalnichevski * TLS upgrade and TLS strategy APIs redesign. Contributed by Oleg Kalnichevski * Added default `ConnectionAcceptor#listen` method that takes an optional attachment as a parameter. Contributed by Oleg Kalnichevski * Upgrade to Java 1.8. Contributed by Oleg Kalnichevski Release 5.1.1 ------------------ This is a maintenance release that corrects a number of defects discovered since release 5.1 including a major defect that can cause a connection pool resource leak. Change Log ------------------- * HTTPCORE-676: Fixed incorrect handling of TLS renegotiation by non-blocking i/o sessions. Contributed by Oleg Kalnichevski * HTTPCORE-616: Make URI authority parsing IPv6 ready. Contributed by Carter Kozak * Improved parsing and formatting of URI components. Contributed by Oleg Kalnichevski * HTTPCORE-673: Fixed incorrect handling of unknown parameters in HTTP/2 SETTINGS frame. Contributed by Oleg Kalnichevski * HTTPCORE-671: URIBuilder does not precent-encode bracketed IPv6 addresses. Contributed by Carter Kozak * HTTPCORE-672: H2ConnPool incorrectly handles validation of closed sessions. Contributed by Oleg Kalnichevski * Fixed race condition when a connection request completes successfully and times out at the same time causing a pool entry leak. Contributed by Oleg Kalnichevski * Fixed TextUtils#toHexString producing incorrect result for negative values. Contributed by Marcono1234 Release 5.1 ----------- This is the first GA release in the 5.1 release series. Notable changes and features included in the 5.1 series: * Conditional conformance with RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax). * Improved support for out of sequence response message handing by the the classic (blocking) HTTP transport. * Improved message builders. Change Log ------------------- * Bug fix: HTTP negotiator factories to accept null TlsStrategy. Contributed by Oleg Kalnichevski * RequestHandlerRegistry to resolve 127.0.0.1 as primary host. Contributed by Oleg Kalnichevski * HTTPCORE-667, HTTPCORE-668, HTTPCORE-670: Added content type and HTTP header constants. Contributed by Arturo Bernal * RFC 3986 conformance: BasicHttpRequest to reject requests whose path component begins with multiple slashes. Contributed by Oleg Kalnichevski * RFC 3986 conformance: BasicHttpRequest to support parsing of valid URI authority components not recognized by java.net.URI. Contributed by Oleg Kalnichevski * Improved message builder support. Contributed by Oleg Kalnichevski * HTTPCORE-666, regression: fixed NPE in ServerHttp1IOEventHandlerFactory. Contributed by Oleg Kalnichevski Release 5.1 BETA3 ------------------ This is likely the last BETA release in the 5.1 release series. The next release is expected to be 5.1 GA. This beta includes a number of new features as well as bug fixes from the stable 5.0.x branch. Notable changes and features included in the 5.1 series: * Conditional conformance with RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax). * Improved support for out of sequence response message handing by the the classic (blocking) HTTP transport. Change Log ------------------- * RFC 3986 conformance: Added `#normalizeSyntax` method to URIBuilder. Contributed by Oleg Kalnichevski * HTTPCORE-663: Added http status code 425. Contributed by Arturo Bernal * HTTPCORE-661: Added http status code 426. Contributed by Arturo Bernal * HTTPCORE-659: Fixed race condition in IOSessionImpl when setting event. Contributed by Nicolas Capponi * HTTPCORE-658: Removed explicit inbound / outbound socket shutdown from the `#close` method of classic HTTP connections. Contributed by Oleg Kalnichevski * HTTPCORE-657: Added TrafficClass to IOReactorConfig (#243). Contributed by Desmond Yeung * Add filters before MAIN_HANDLER so they won't be ignored (#236). Contributed by Rob Spoor * Bug fix: Fixed broken result callback in ProtocolNegotiatorBase. Contributed by Oleg Kalnichevski * Fixed NPE and improve code centralization in URIBuilder (#235). Contributed by Lars Helge Øverland * Added `#appendPath` and `#appendPathSegments` methods to URIBuilder (#234). Contributed by Lars Helge Øverland * Removed connection acceptor API changes (moved to 5.2). Contributed by Oleg Kalnichevski * Removed protocol upgrade API changes (moved to 5.2). Contributed by Oleg Kalnichevski * Bug fix: Do not attempt to shut down non-blocking TLS session gracefully in case the TLS handshake has not been fully completed. Contributed by Oleg Kalnichevski Release 5.1 BETA2 ------------------ This is the second BETA release in the 5.1 release series that includes a number of new features as well as bug fixes from the stable 5.0.x branch. Change Log ------------------- * PR #232: Master try w res and more. - Use try-with-resources. - Use diamonds. - Add missing @Override. - Make better use of Map APIs. - Remove redundant modifiers. - Use Collections.addAll() API instead of loops. - Remote extra semicolons (;). Contributed by Gary Gregory * Bug fix: Fixed ignoring maxResultLength in toString method of EntityUtils. Contributed by Klaw * Bug fix: corrected handling of pushed stream refusal by the HTTP/2 protocol handler. Contributed by Oleg Kalnichevski * Improved connection re-use (keep-alive) tracking by the benchmark; disabled H2 push when running the benchmark with HTTP/2. Contributed by Oleg Kalnichevski * Improved detection of disconnected endpoints by HttpAsyncRequester. Contributed by Oleg Kalnichevski * HTTPCORE-626: in case of an unsuccessful `expect-continue` handshake do not make any attempts to keep the client connection in a consistent state if the server intends to close it on its end. Contributed by Oleg Kalnichevski * Move decision if the i/o session is to be terminated immediately to the protocol handler by default attempt to terminate the i/o session gracefully. Contributed by Oleg Kalnichevski * Optimized request termination by the non-blocking HTTP/1.1 protocol handler in case of a premature response or `expect-continue` handshake failure. Contributed by Oleg Kalnichevski * `AbstractAsyncResponseConsumer` to handle gracefully an inconsistent state caused by the caller signalling a response with no entity but subsequently calling methods to consume data. Contributed by Oleg Kalnichevski * Revised HTTP protocol negotiation for non-blocking I/O sessions Contributed by Oleg Kalnichevski * Revised TLS session initialization in I/O reactor code Contributed by Oleg Kalnichevski * HTTPCORE-653: CancelledKeyException triggers I/O reactor shutdown. Contributed by Oleg Kalnichevski * Make `ReactiveDataConsumer#failed` a no-op if it has already been marked as complete (#227). Contributed by Colin Weld * HTTPCORE-644: non-blocking TLSv1.3 connections can end up in an infinite event spin when closed concurrently by the local and the remote endpoints. Contributed by Oleg Kalnichevski * Calculate pendingOutputRequests before calling produceOutput to avoid race condition. Contributed by Colin Weld Release 5.1 BETA1 ------------------ This is the first BETA release in the 5.1 release series that includes a number of new features as well performance optimizations in the classic HTTP transport. Notable changes and features included in the 5.1 series: * Conditional conformance with RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax). * Improved support for out of sequence response message handing by the the classic (blocking) HTTP transport. Change Log ------------------- * HTTPCORE-649: Implement ByteArrayBuffer.append(ByteBuffer) Contributed by Carter Kozak * Use decimal numbers for endpoint/execution IDs Contributed by Michael Osipov * HTTPCORE-645: Increase blocking default chunk size from 2 KiB to 8 KiB. Contributed by Carter Kozak * HTTPCORE-645: Chunked request streams reuse buffers between requests. Contributed by Carter Kozak * HTTPCORE-639: Add a configurable ResponseOutOfOrder strategy for DefaultBHttpClientConnection. Contributed by Carter Kozak * RFC 3986 conformance: Support percent-encoded reserved characters in the host component. Contributed by Oleg Kalnichevski * RFC 3986 conformance: Revised URI parsing and formatting. Contributed by Oleg Kalnichevski * Better parse and format methods for URIAuthority. Contributed by Oleg Kalnichevski * HTTPCORE-628: Do not encode blanks as + in URI query component. Contributed by Oleg Kalnichevski * Async connection listeners to support passing attachments to endpoints. Contributed by Oleg Kalnichevski Release 5.0.2 ------------------ This release reverts changes to early response handling logic introduced in 5.0.1 and fixes a number of minor defects. Improvement of the early response handling by the classic client protocol handler has been moved to 5.1. Please also note that as of this release HttpCore will use a 3 minute socket timeout by default. Change Log ------------------- * HTTP/1.1 protocol handlers fail to consistently set actual protocol version in the execution context. Contributed by Oleg Kalnichevski * HTTPCORE-648: Buffer array access respects the arrayOffset (#220). Contributed by Carter Kozak * HTTPCORE-646: ChunkedInputStream avoids creating unnecessary buffers. Contributed by Carter Kozak * Revert "Improved handling of early response messages by the classic client protocol handler". Contributed by Oleg Kalnichevski * HTTPCORE-638: SharedOutputBuffer must trigger DataStreamChannel#endStream() once (#204). Contributed by malaysf * Fixed integer overflow in IOWorkers selector (#203). Contributed by Pavel kaplin * HTTPCLIENT-2098: EntityUtils#toByteArray method fails to take into account `maxResultLength` parameter (#202). Contributed by Andriy * Add Automatic-Module-Name to the manifest. Contributed by Niels Basjes * HTTPCORE-636: Logging statements use slf4j interpolation over concatenation (#199). Contributed by Carter Kozak * HTTPCLIENT-2091: Use finite (3 minutes) socket timeout by default. Contributed by Oleg Kalnichevski Release 5.0.1 ------------------ This release improves handling of early response messages by the classic client protocol handler and fixes a number of minor defects. Change Log ------------------- * Add handling of early response messages by the classic client protocol handler. Contributed by Oleg Kalnichevski * HTTPCORE-631: Revised i/o reactor shutdown sequence and resource de-allocation. Contributed by Oleg Kalnichevski * Fix CharArrayBuffer.subSequence(beganIndex,endIndex) to return right result. Contributed by Lee Ray * Added exception callback to async server implementations enabling logging of unexpected and fatal exceptions in the server side protocol handlers. Contributed by Oleg Kalnichevski Release 5.0 ------------------ This is the first stable (GA) release of HttpCore 5.0. Notable changes and features included in the 5.0 series: * Support for HTTP/2 protocol and conformance to requirements and recommendations of the latest HTTP/2 protocol specification (RFC 7540, RFC 7541) Supported features: ** HPACK header compression ** stream multiplexing (client and server) ** flow control ** response push (client and server) ** message trailers ** expect-continue handshake ** connection validation (ping) ** application-layer protocol negotiation (ALPN) on Java 9+ ** TLS 1.2 security features Features out of scope for 5.0 release: ** padding of outgoing frames ** stream priority ** plain connection HTTP/1.1 upgrade ** CONNECT method * Improved conformance to requirements and recommendations of the latest HTTP/1.1 protocol specification (RFC 7230, RFC 7231) * New asynchronous HTTP transport APIs consistent for both HTTP/1.1 and HTTP/2 transport. * Redesigned I/O reactor APIs and improved NIO based reactor implementation for a greater performance and scalability. * Support for server-side request filters for classic and asynchronous server implementations. Request filters could be used to implement cross-cutting protocol aspects such as the 'expect-continue' handshaking and user authentication / authorization. * Support for Reactive Streams API [http://www.reactive-streams.org/] * Redesigned connection pool implementation with strict connection limit guarantees. The connection pool is expected to have a better performance under higher concurrency due to reduced global pool lock contention. * New connection pool implementation with lax connection limit guarantees and better performance under higher concurrency due to absence of a global pool lock. * Package name space changed to 'org.apache.hc.core5' * Maven group id changed to 'org.apache.httpcomponents.core5' HttpCore 5.0 releases can be co-located with earlier versions. Change Log ------------------- * GitHub #183: IO reactor status equality clean ups. Contributed by Gary Gregory * HTTPCORE-623 GitHub #185: Change org.apache.hc.core5.http.io.BHttpConnection.isDataAvailable() int input to Timeout. Contributed by Gary Gregory * GitHub #188: Add PathEntity, a Path based implementation of HttpEntity. (#188) Contributed by Gary Gregory * GitHub #191: Add org.apache.hc.core5.http.Method.normalizedValueOf(String). Contributed by Gary Gregory * GitHub #193: Reuse constants instead of magic strings. Contributed by Gary Gregory Release 5.0-BETA11 ------------------ This BETA improves handling of illegal or invalid requests on the server-side and fixes a number of defects in HTTP/2 protocol code found since the last release. IMPORTANT: This release is expected to be the last BETA version. If no major design flaws are found, the actual 5.0 API will be frozen and the next version will be promoted to GA. Change Log ------------------- * HTTPCORE-618: org.apache.hc.core5.http.HttpStatus should be a class, not an interface. Contributed by Gary Gregory * HTTPCORE-619: org.apache.hc.core5.reactor.EventMask should be a class, not an interface. Contributed by Gary Gregory * Normalize TimeUnit in TimeValue's #equals() and #hashCode(). Contributed by Michael Osipov * Support for status code 431 (Request Header Fields Too Large) by H2 server-side protocol handler Contributed by Oleg Kalnichevski * Enforce H2 SETTINGS_MAX_HEADER_LIST_SIZE limit for HTTP/2 messages. Contributed by Oleg Kalnichevski * Improved H2 connection window management logic. Contributed by Ryan Schmitt * Use UTF-8 encoding for XML-based content types. Contributed by Michael Osipov * Bug-fix: H2 protocol handler to reset all streams unconditionally in case of an unexpected exception and attempt to close the i/o session gracefully if possible. Contributed by Oleg Kalnichevski * Support for status code 431 (Request Header Fields Too Large). Contributed by Oleg Kalnichevski * Corrected handling of illegal or invalid request heads by async server-side protocol handler Contributed by Oleg Kalnichevski * Bug-fix: Fixed race condition in ReactiveDataConsumer. Contributed by Ryan Schmitt * Bug-fix: Fixed H2 SETTINGS_HEADER_TABLE_SIZE negotiation. Contributed by Ryan Schmitt * Bug-fix: Fixed empty response handling in ReactiveResponseConsumer. Contributed by Ryan Schmitt * Allow for timeout while acquiring lock in StrictConnPool. Contributed by Chris Wildman * Added H2 config option to disable Huffman compression Contributed by Oleg Kalnichevski * HTTPCORE-613: Now allowing 0 for validaterAfterInactivity. Contributed by Peter Frank * HTTPCLIENT-2029: URIBuilder to support parsing of non-UTF8 URIs. Contributed by Oleg Kalnichevski * HTTPCLIENT-2026: Fixed URIBuilder#isOpaque() logic. Contributed by Oleg Kalnichevski * HTTPCORE-611: Minor glitches with TimeValue. Contributed by Gary Gregory Release 5.0-BETA10 ------------------- This BETA fixes a number of defects found since the last release. IMPORTANT: This release is expected to be the last BETA version. If no major design flaws are found, the actual 5.0 API will be frozen and the next version will be promoted to GA. Change Log ------------------- * HTTPCORE-608: Fixes regression that can lead to performance degradation of asynchronous HTTP/1.1 protocol handlers. Contributed by Oleg Kalnichevski * HTTPCORE-606: HTTP/2 protocol handler incorrectly uses larger frame size prior to SETTING handshake completion; revision of SETTINGS handshake implementation. Contributed by Oleg Kalnichevski * Bug fix: HTTP/2 Protocol handler does not update global connection output window in some cases. Contributed by Oleg Kalnichevski * Upgraded Conscrypt dependency to version 2.2.1 Contributed by Oleg Kalnichevski Release 5.0-BETA9 ------------------- This BETA fixes a number of defects found since the last release, improves behavior of the lax (concurrent) connection pools (special thanks to Linton Miller), simplifies and improves input event handling of SSL/TLS sessions and the HTTP/1.1 protocol event handler. Please note that the following interfaces have marked as internal as of this release: IOSession, IOEventHandler, IOEventHandlerFactory. IMPORTANT: This release is expected to be the last BETA version. If no major design flaws are found, the actual 5.0 API will be frozen and the next version will be promoted to GA. Change Log ------------------- * HTTPCORE-604: async HTTP/1.1 protocol handler must close the i/o session if it cannot be kept alive before passing control to the message exchange hanlder. Contributed by Oleg Kalnichevski * Simplification of HTTP/1.1 read event handling logic. Contributed by Oleg Kalnichevski * Redesign of SSL/TLS async I/O event handling. Contributed by Oleg Kalnichevski * HTTPCLIENT-2016 (regression): Tab chars are replaced by question marks in header values. Contributed by Oleg Kalnichevski * Bug fix: Fixed capacity calculation in SessionInputBufferImpl#put. Contributed by Oleg Kalnichevski * Marked IOSession, IOEventHandler and IOEventHandlerFactory interfaces as internal. Contributed by Oleg Kalnichevski * Propagate BasicResponse{Consumer,Producer}#failed() to data{Consumer,Producer}. Contributed by Roy Hashimoto * Regression: Fixed regression in SingleCoreIOReactor leading to unhandled CancelledKeyException and causing abnormal I/O reactor termination. Contributed by Oleg Kalnichevski * HTTPCORE-601: Work-around for SSL session spin on outbound session closure with Conscrypt 2.2.1. Contributed by Oleg Kalnichevski * HTTPCORE-600: SSLIOSession incorrectly disables input interest when there is still decrypted data available in the session input buffer. Contributed by Oleg Kalnichevski * Added statusClass property to StatusLine. Contributed by Behrang <18451+behrangsa at users.noreply.github.com> * HTTPCORE-596: Connection pools to use an optional callback to close out disposed connections. Contributed by Oleg Kalnichevski * Connection pools to close connections gracefully in case of an ordinary connection disposal. Contributed by Oleg Kalnichevski * HTTPCORE-599: I/O sessions to stop reading from the underlying network channel of READ interest is disabled. Contributed by Oleg Kalnichevski * TimeValue to implement Comparable. Contributed by Oleg Kalnichevski * HTTPCORE-597: AsyncEntityProducers#createText always throws IllegalArgumentException. Contributed by Oleg Kalnichevski * HTTPCORE-595: Tolerate NPE thrown by com.android.org.conscrypt.NativeCrypto#SSL_get_shutdown in Android Conscrypt. Contributed by Oleg Kalnichevski * Set accessibility flag on reflected methods in ReflectionUtils. Contributed by Richard Hernandez * Restore original socket timeout after finishing SSL/TLS handshake. Contributed by Richard Hernandez * Add SOAP XML content type. Contributed by Raihaan Shouhell * HTTPCORE-593, HTTPCORE-592, HTTPCORE-590, HTTPCORE-589: LaxConnPool improvements. Contributed by Linton Miller * HTTPCORE-588: Race condition in ComplexCancellable that can lead to operational dependency not being correctly cancelled. Contributed by Oleg Kalnichevski * Execute Socket[Channel]#connect under doPrivileged. Contributed by Simon Willnauer * Fail request execution immediately after submission if the I/O session has already been closed. Contributed by Oleg Kalnichevski * Bug fix: Fixed regression in the HTTP protocol negotiators. Contributed by Oleg Kalnichevski * Bug fix: MessageSupport#formatTokens no longer modifies the array of tokens passed to it as a parameter. Contributed by Oleg Kalnichevski * HTTPCORE-586: Improve LaxConnPool max pool size management. Contributed by Linton Miller * HTTPCORE-585: Fix LaxConnPool#servicePendingRequest to avoid losing pending lease requests it can't fulfill. Contributed by Linton Miller * Possible fix for iteration thread-safety issue. Contributed by Linton Miller * SSL session verifier for classic HTTP requester. Contributed by Oleg Kalnichevski * Bug fix: non-blocking I/O event handler may be null in case of an unexpected exception. Contributed by Oleg Kalnichevski * Bug fix: Corrected immediate termination of non-blocking I/O sessions. Contributed by Oleg Kalnichevski * Bug fix: Ensure consistency of internal buffers in case of I/O or SSL exception. Contributed by Oleg Kalnichevski Release 5.0-BETA8 ------------------- This BETA fixes a number of defects found since the last release and adds several convenience factory and builder classes, mainly for TLS configuration and HTTP message construction. As of this version all server and requester implementations exclude weak TLS protocol versions and ciphers. Please note that the following interfaces have changed: ResponseChannel, AsyncRequestConsumer, AsyncResponseConsumer, HttpContentProducer, SSLServerSetupHandler. IMPORTANT: This release is expected to be the last BETA version. If no major design flaws are found the actual 5.0 API will be frozen and the next version will be promoted to GA. Change Log ------------------- * HTTPCORE-582: Fixed NPE in SingleCoreIOReactor#processEvents Contributed by Oleg Kalnichevski * Redesign of fatal exception handling by the I/O reactors Contributed by Oleg Kalnichevski * HTTPCORE-581: Wrong deadline calculation for non-blocking I/O operations Contributed by Oleg Kalnichevski * Better HttpVersion and ProtocolVersion. (#131) Contributed by Gary Gregory * Classic and async message builders. Contributed by Oleg Kalnichevski * Improved classic and async entity factory methods. Contributed by Oleg Kalnichevski * HTTPCORE-578: Incorrect serialization of HeaderGroup. Contributed by Gary Gregory * HTTPCORE-577: server-side HTTP protocol negotiator to propagate exceptions to the I/O event handler associated with the I/O session. Contributed by Oleg Kalnichevski * HTTPCLIENT-1986: URIBuilder#isPathEmpty method to verify if encodedPath is an empty string. Contributed by Oleg Kalnichevski * SSLIOSession: Improve connectTimeout implementation. Contributed by Ryan Schmitt * HTTPCLIENT-1981: Disallow TRACE requests with an enclosed entity. Contributed by Oleg Kalnichevski * HTTPCLIENT-1978: Filter characters before byte conversion. Contributed by Ryan Schmitt * HTTPCORE-573: FileContentDecoder don't always enforce the maximum number of bytes to transfer. Contributed by Julien Coloos * All server and requester implementation to exclude weak TLS protocol versions and ciphers. Contributed by Oleg Kalnichevski * Improved SSL setup handling for classic requester and server. Contributed by Oleg Kalnichevski * Common TLS support methods migrated from HttpClient. Contributed by Oleg Kalnichevski * Bug fix: non-blocking SSL I/O sessions fail to fire session disconnected event. Contributed by Oleg Kalnichevski * Bug fix: basic entity consumers to clear buffered content when releasing resources. Contributed by Oleg Kalnichevski * conscrypt-openjdk-uber 1.4.1 -> 1.4.2. Contributed by Gary Gregory * Provide more information when a BindException occurs. Contributed by Gary Gregory * Refactor ClassicHttpRequest and ClassicHttpResponse interfaces to extend a new interface HttpEntityContainer. Contributed by Gary Gregory * Update RxJava from 2.2.7 to 2.2.8. Contributed by Gary Gregory Release 5.0-BETA7 ------------------- This BETA release adds support for SOCKS version 5, improves support for TLS handshake timeout configuration, improves URI builder, and fixes various defects. Please note that the following interfaces have changed: TlsStrategy, IOSession, TransportSecurityLayer. Change Log ------------------- * Add a thread-safe capacity channel to AbstractHttp1StreamDuplexer Contributed by Richard Hernandez * HTTPCORE-568: Signal capacity in ReactiveDataConsumer whenever possible (fixes a race condition) Contributed by Richard Hernandez * Made standard HttpEntity implementations immutable / conditionally immutable Contributed by Oleg Kalnichevski * HTTPCLIENT-1968: URIBuilder to split path component into path segments when digesting a URI Contributed by Oleg Kalnichevski * HTTPCLIENT-1968: added utility methods to parse and format URI path segments Contributed by Oleg Kalnichevski * Added SOCKS proxy support to classic (blocking) HTTP requester Contributed by Oleg Kalnichevski * Made AbstractBinAsyncEntityProducer and AbstractCharAsyncEntityProducer conditionally threading-safe Contributed by Oleg Kalnichevski * Added convenience method to test if ContentType instances are of the same MIME type Contributed by Oleg Kalnichevski * Merge connect and handshake timeouts in AbstractIOSessionPool Contributed by Ryan Schmitt * HTTPCLIENT-1960: URIBuilder incorrect handling of multiple leading slashes in path component Contributed by Oleg Kalnichevski * HTTPCLIENT-1959: corrected argument validation in HttpHost constructors; removed deprecated methods Contributed by Oleg Kalnichevski * org.apache.hc.core5.reactor.IOSession: Deprecate lock() in favor of getLock(). Add missing @Override. Contributed by Gary Gregory * HTTPCORE-563: client support for SOCKS version 5 Contributed by David Maplesden * Delay I/O operations on non-blocking SSL sessions until fully initialized Contributed by Oleg Kalnichevski * Add `handshakeTimeout` support throughout Contributed by Ryan Schmitt * Updated the set of project report generated by Maven Contributed by Oleg Kalnichevski * SSLIOSession: Add `connectTimeout` constructor param Contributed by Ryan Schmitt * Update RxJava from 2.1.9 to 2.2.7. Contributed by Gary Gregory Release 5.0-BETA6 ------------------- This BETA release adds support for advanced TLS functions (such as ALPN protocol negotiation) on Java 1.7 and Java 1.8 through Conscrypt TLS library, and fixes a number of defects found since the previous release. Please note AsyncDataConsumer interface has been changed in order to make it simpler to implement. However existing AsyncDataConsumer might require minor modifications. Change Log ------------------- * Removed OSGi module. Contributed by Oleg Kalnichevski * Added support for advanced TLS functions (such as ALPN extension) on Java 1.7 and Java 1.8 through Conscrypt TLS library. Contributed by Oleg Kalnichevski * HTTP/1.1 and HTTP/2 async protocol handlers to use I/O session lock for output synchronization. Contributed by Oleg Kalnichevski * Bug fix: non-blocking HTTP/1.1 server-side streams incorrectly report their keep-alive status. Contributed by Oleg Kalnichevski * Bug fix: fixed a race condition in non-blocking HTTP/1.1 protocol handlers causing premature clearing of output interest. Contributed by Oleg Kalnichevski * Simplified AsyncDataConsumer interface contract Contributed by Oleg Kalnichevski * HTTPCORE-560: Fix LaxConnPool leasing incorrect PoolEntry when processing pending requests Contributed by Desmond Yeung * Deprecate and rename org.apache.hc.core5.util.Timeout.ofMillis(long) to ofMilliseconds(long). Contributed by Gary Gregory * Deprecate and rename org.apache.hc.core5.util.TimeValue.ofMillis(long) to ofMilliseconds(long). Contributed by Gary Gregory * Fix org.apache.hc.core5.util.TimeValue.convert(TimeUnit). Contributed by Gary Gregory * Add org.apache.hc.core5.util.TimeValue.divide(*). Contributed by Gary Gregory * org.apache.hc.core5.http.URIScheme.getId() Contributed by Gary Gregory * HTTPCORE-562: The reason phrase returned by org.apache.hc.core5.http.HttpResponse.getReasonPhrase() may be empty. Contributed by Gary Gregory * TimeValue can parse more leniently: Allow spaces and singular words for time units, for example " 1 SECOND ". Contributed by Gary Gregory * Add HttpStreamResetException.serialVersionUID Contributed by Gary Gregory * Add org.apache.hc.core5.http.HttpRequest.setUri(URI) Contributed by Gary Gregory * org.apache.hc.core5.net.URIBuilder.setHttpHost(HttpHost) Contributed by Gary Gregory * Deprecate constructors in org.apache.hc.core5.http.HttpHost that do not have the scheme argument first in favor of new constructors that do. Contributed by Gary Gregory * Add org.apache.hc.core5.http.HttpHost.create(URI) Contributed by Gary Gregory * Keep the entries in org.apache.hc.core5.http.protocol.UriPatternMatcher#map in insertion order. Contributed by Gary Gregory Release 5.0-BETA5 ------------------- This BETA release fixed a severe regression introduced in 5.0-BETA4. Release 5.0-BETA4 ------------------- This BETA release adds support for Reactive Streams API [http://www.reactive-streams.org/] and fixes compatibility issues with Java 11 new TLS engine as well as a number of defects found since the previous release. This release also includes a redesigned HTTP stress test tool loosely based on Apache Benchmark (AB) command interface with support for HTTP/2. Change Log ------------------- * Resolved compatibility issues with TLS 1.3 engine shipped with Java 11 Contributed by Oleg Kalnichevski * Bug fix: corrected handling of FORCE_HTTP2 version policy by the server-side protocol negotiator Contributed by Oleg Kalnichevski * Redesign of the internal HTTP benchmark loosely based on AB, added support for HTTP/2 stress testing. Contributed by Oleg Kalnichevski * Regression: fixed the response connection control interceptor incorrectly using 'Connection: keep-alive' directive with HTTP/1.0 requests that do not have an explicit 'Connection' request header. Contributed by Oleg Kalnichevski * HTTPCORE-552, HTTPCORE-553, HTTPCORE-554, HTTPCORE-555, HTTPCORE-556, HTTPCORE-557, HTTPCORE-558: added status codes 103, 208, 226, 308, 451, 506, 508, 510 Contributed by sparsick and Georg Berky * HTTPCLIENT-1942: Add support for Reactive Streams Contributed by Ryan Schmitt * Respect CloseMode when closing async TCP sockets Contributed by Ryan Schmitt * Fix typo in message generated by org.apache.hc.core5.io.SocketTimeoutExceptionFactory.toMessage(int). Contributed by Gary Gregory * Update the org.apache.hc.core5.http.protocol.HttpContext.setAttribute(String, Object) API to return the previous value. Contributed by Gary Gregory * Deprecate and rename org.apache.hc.core5.http.EndpointDetails.getSocketTimeout() to getSocketTimeoutMillis(). Contributed by Gary Gregory * Deprecate and rename org.apache.hc.core5.http.MessageHeaders.getAllHeaders() to getHeaders(). Contributed by Gary Gregory * Deprecate and rename org.apache.hc.core5.http.MessageHeaders.getSingleHeaders(String) to getHeader(String). Contributed by Gary Gregory Release 5.0-BETA3 ------------------- This BETA release fixes a number of defects found since the previous release, adds several incremental improvements and improves Javadoc documentation. Change Log ------------------- * Improved information responses (1xx) processing. Contributed by Kirill Usov * Bug fix: fixed AbstractAsyncResponseConsumer to handle null callback parameter Contributed by Oleg Kalnichevski * Bug fix: HTTP stream handlers no longer call #failed event of the associated exchange handler when the message exchange has already been completed. Contributed by Oleg Kalnichevski * Bug fix: fixed NPE caused by null timeout attribute of IOSessionRequest Contributed by Oleg Kalnichevski * Bug fix: fixed incorrect propagation of exception cause in case of an HTTP protocol violation during HTTP protocol version negotiation Contributed by Oleg Kalnichevski * Workaround for misbehaved servers that return HTTP 204 responses with a content Contributed by Alessandro Gherardi * HTTPCORE-510: Avoid an ArithmeticException in AbstractMultiworkerIOReactor by failing earlier by checking ioThreadCount in IOReactorConfig constructor. Contributed by Gary Gregory * HTTPCORE-511: Do not cache result of Runtime.getRuntime().availableProcessors() in IOReactorConfig. Contributed by Gary Gregory * HTTPCORE-509: AVAIL_PROCS is auto-configured based on core count. Contributed by Gary Gregory * HTTPCORE-514: Exceptions defined by HttpCore should clean message strings when built to replace non-printable characters with hex values. Contributed by Gary Gregory * HTTPCORE-517: Allow SecurityManager to stop socket connections. Contributed by Gary Gregory and Paul Thompson * HTTPCORE-537: org.apache.hc.core5.http.message.BasicHttpResponse.toString() prints its code twice and no protocol version. Contributed by Gary Gregory * HTTPCORE-539: Constructing a new FileEntityProducer for a file whose length is greater than 2GB throws an IllegalArgumentException. Contributed by Gary Gregory * HTTPCORE-540: EndpointDetails implements HttpConnectionMetrics. Contributed by Gary Gregory * HTTPCORE-541: Add HttpVersion.ALL for all HTTP versions known to HttpCore. Contributed by Gary Gregory * HTTPCORE-542: Add missing org.apache.hc.core5.http.message.BasicClassicHttpRequest.serialVersionUID Contributed by Gary Gregory * HTTPCORE-544: Add org.apache.hc.core5.http.EndpointDetails.getSocketTimeout() Contributed by Gary Gregory * HTTPCORE-545: Add org.apache.hc.core5.http.message.HeaderGroup.removeHeaders(Header) Contributed by Gary Gregory * HTTPCORE-546: Update org.apache.hc.core5.http.message.HeaderGroup.removeHeader(Header) to return a boolean Contributed by Gary Gregory * HTTPCORE-547: Update org.apache.hc.core5.http.message.HeaderGroup.removeHeaders(String) to return a boolean Contributed by Gary Gregory * HTTPCORE-548: Add missing HttpContext parameter to APIs. Contributed by Gary Gregory * Refactor duplicate messages into a new 0-arg constructor for StreamClosedException. Contributed by Gary Gregory * Refactor timeout APIs to include the actual timeout value. Contributed by Gary Gregory * Refactor timeout APIs to include the scale in the method name; for example 'int getSocketTimeout()' vs. int 'getSocketTimeoutMillis()'. Contributed by Gary Gregory * HTTPCORE-550: When a ParseException is caught and rethrown as an IOException in ChunkDecoder#processFooters(), the IOException does not chain the original ParseException. Contributed by Gary Gregory * Request specific push consumers Contributed by Oleg Kalnichevski * HTTPCLIENT-1927: URLEncodedUtils#parse breaks at double quotes when parsing unquoted values Contributed by Oleg Kalnichevski * HTTPCORE-528: SSL I/O session spins upon abnormal connection closure by the opposite endpoint. Contributed by Oleg Kalnichevski Release 5.0-BETA2 ------------------- This BETA release fixes a number of defects found since the previous release and adds several incremental improvements. Notable changes and features included in the 5.0 series: * Support for HTTP/2 protocol and conformance to requirements and recommendations of the latest HTTP/2 protocol specification (RFC 7540, RFC 7541) Supported features: ** HPACK header compression ** stream multiplexing (client and server) ** flow control ** response push (client and server) ** message trailers ** expect-continue handshake ** connection validation (ping) ** application-layer protocol negotiation (ALPN) on Java 9+ ** TLS 1.2 security features Features out of scope for 5.0 release: ** padding of outgoing frames ** stream priority ** plain connection HTTP/1.1 upgrade ** CONNECT method * Improved conformance to requirements and recommendations of the latest HTTP/1.1 protocol specification (RFC 7230, RFC 7231) * New asynchronous HTTP transport APIs consistent for both HTTP/1.1 and HTTP/2 transport. * Redesigned I/O reactor APIs and improved NIO based reactor implementation for a greater performance and scalability. * Support for server-side request filters for classic and asynchronous server implementations. Request filters could be used to implement cross-cutting protocol aspects such as the 'expect-continue' handshaking and user authentication / authorization. * Redesigned connection pool implementation with strict connection limit guarantees. The connection pool is expected to have a better performance under higher concurrency due to reduced global pool lock contention. * New connection pool implementation with lax connection limit guarantees and better performance under higher concurrency due to absence of a global pool lock. * Package name space changed to 'org.apache.hc.core5' * Maven group id changed to 'org.apache.httpcomponents.core5' HttpCore 5.0 releases can be co-located with earlier versions. The 5.0 APIs are considered feature complete and are not expected to undergo any major changes anymore. The focus of development is now shifting to API polish, code stabilisation and documentation improvements. Change Log ------------------- * HTTP/2 multiplexed requester to support cancellation of individual message exchanges without termination of the underlying I/O session. Contributed by Oleg Kalnichevski * Bug fix: corrected handling of GOAWAY frames by HTTP/2 stream multiplexer Contributed by Oleg Kalnichevski * Bug fix: prevent a tight loop in non-blocking SSL I/O sessions due to a HTTP/2 frame fragment in the SSL input buffer. Contributed by Oleg Kalnichevski * Bug fix: incorrect handing of premature I/O session termination by the server-side application protocol negotiator. Contributed by Oleg Kalnichevski * Strict / lax ALPN handshake mode for HTTP/2 multiplexing requester. Contributed by Oleg Kalnichevski * HTTPCORE-496: Add API org.apache.http.protocol.UriPatternMatcher.entrySet(). Contributed by Gary Gregory * HTTPCORE-499 Make interface Header extend NameValuePair Contributed by Gary Gregory * HTTPCORE-501 org.apache.http.client.utils.URLEncodedUtils.parse() should return a new ArrayList when there are no query parameters. Contributed by Gary Gregory Release 5.0-BETA1 ------------------- This is a major release that renders HttpCore API incompatible with the stable 4.x branch and upgrades HTTP/1.1 and HTTP/2 protocol conformance to the requirements and recommendations of the latest protocol specification. Notable new features in this release: * New HTTP/2 requester optimized for multiplexed execution of requests. Notable changes and features included in the 5.0 series: * Support for HTTP/2 protocol and conformance to requirements and recommendations of the latest HTTP/2 protocol specification (RFC 7540, RFC 7541) Supported features: ** HPACK header compression ** stream multiplexing (client and server) ** flow control ** response push (client and server) ** message trailers ** expect-continue handshake ** connection validation (ping) ** application-layer protocol negotiation (ALPN) on Java 9+ ** TLS 1.2 security features Features out of scope for 5.0 release: ** padding of outgoing frames ** stream priority ** plain connection HTTP/1.1 upgrade ** CONNECT method * Improved conformance to requirements and recommendations of the latest HTTP/1.1 protocol specification (RFC 7230, RFC 7231) * New asynchronous HTTP transport APIs consistent for both HTTP/1.1 and HTTP/2 transport. * Redesigned I/O reactor APIs and improved NIO based reactor implementation for a greater performance and scalability. * Support for server-side request filters for classic and asynchronous server implementations. Request filters could be used to implement cross-cutting protocol aspects such as the 'expect-continue' handshaking and user authentication / authorization. * Redesigned connection pool implementation with strict connection limit guarantees. The connection pool is expected to have a better performance under higher concurrency due to reduced global pool lock contention. * New connection pool implementation with lax connection limit guarantees and better performance under higher concurrency due to absence of a global pool lock. * Package name space changed to 'org.apache.hc.core5' * Maven group id changed to 'org.apache.httpcomponents.core5' HttpCore 5.0 releases can be co-located with earlier versions. The 5.0 APIs are considered feature complete and are not expected to undergo any major changes anymore. The focus of development is now shifting to API polish, code stabilisation and documentation improvements. Change Log ------------------- * HTTP/2 multiplexing requester. Contributed by Oleg Kalnichevski * Compatibility with Java 9 (tested with Oracle JDK 9.0.1). Contributed by Oleg Kalnichevski * Fixed handling of relative request paths in BasicHttpRequest. Contributed by Oleg Kalnichevski * HTTPCORE-494: Add image constants to ContentType. Contributed by Gary Gregory * HTTPCORE-486: set a time limit on processing of pending I/O events by I/O reactors. Contributed by xiaohu-zhang Release 5.0-ALPHA4 ------------------- This is a major release that renders HttpCore API incompatible with the stable 4.x branch and upgrades HTTP/1.1 and HTTP/2 protocol conformance to the requirements and recommendations of the latest protocol specification. Notable changes and features included in the 5.0 series are: * Support for HTTP/2 protocol and conformance to requirements and recommendations of the latest HTTP/2 protocol specification (RFC 7540, RFC 7541) Supported features: ** HPACK header compression ** stream multiplexing (client and server) ** flow control ** response push (client and server) ** message trailers ** expect-continue handshake ** connection validation (ping) ** application-layer protocol negotiation (ALPN) on Java 1.9+ ** TLS 1.2 security features Features out of scope for 5.0 release: ** padding of outgoing frames ** stream priority ** plain connection HTTP/1.1 upgrade ** CONNECT method * Improved conformance to requirements and recommendations of the latest HTTP/1.1 protocol specification (RFC 7230, RFC 7231) * New asynchronous HTTP transport APIs consistent for both HTTP/1.1 and HTTP/2 transport. * Redesigned I/O reactor APIs and improved NIO based reactor implementation for a greater performance and scalability. * Support for server-side request filters for classic and asynchronous server implementations. Request filters could be used to implement cross-cutting protocol aspects such as the 'expect-continue' handshaking and user authentication / authorization. * Redesigned connection pool implementation with strict connection limit guarantees. The connection pool is expected to have a better performance under higher concurrency due to reduced global pool lock contention. * New connection pool implementation with lax connection limit guarantees and better performance under higher concurrency due to absence of a global pool lock. * Package name space changed to 'org.apache.hc.core5' * Maven group id changed to 'org.apache.httpcomponents.core5' HttpCore 5.0 releases can be co-located with earlier versions. Please note that at this point 5.0 APIs are considered API experimental and unstable and are expected to change in the coming releases without providing a migration path. Change Log ------------------- * HTTPCORE-485: Reduced memory footprint of non-blocking SSL sessions by 25%. Contributed by Todor Bonchev <31352118+todorbonchev at users.noreply.github.com> * Fixed propagation of entity details of incoming HTTP/1.1 messages by non-blocking HTTP/1.1 stream duplexer Contributed by Oleg Kalnichevski * Configurable connection pool concurrency policy Contributed by Oleg Kalnichevski * HTTPCORE-390: Connection pool implementation with higher concurrency characteristics and lax total and per route max guarantees. Contributed by Oleg Kalnichevski * Request filters for classic and asynchronous server-side protocol handlers Contributed by Oleg Kalnichevski * Improved handling of 1xx status messages by the classic transport; server expectation (expect-continue) handshake can now be implemented as a cross-cutting aspect by both the classic and asynchronous transports Contributed by Oleg Kalnichevski * Fixed bug in the classic (blocking) HTTP requester causing incorrect release of connections in case a response message has no entity (such as 204) Contributed by Oleg Kalnichevski * HTTPCORE-454: use Timeout class to represent timeout values Contributed by Oleg Kalnichevski * Made HTTP/2 data window update precede sending of the DATA frame to the opposite endpoint. In some extreme circumstances the opposite endpoint can send back a WINDOW_UPDATE frame in-between of these two operation causing the data window value to exceed its maximum valid value Contributed by Oleg Kalnichevski * I/O interest flag in IOSessionImpl requires synchronization instead of atomic operation in order to avoid race condition Contributed by Oleg Kalnichevski * HTTPCORE-472: Fixed problem with blocking message parsers incorrectly throwing "Maximum line length limit exceeded" exception in some corner cases Contributed by Oleg Kalnichevski * Improved I/O reactor APIs with a smaller public API footprint Contributed by Oleg Kalnichevski * Rewrite of I/O reactor internal channel management; more efficient handling of outgoing connection requests Contributed by Oleg Kalnichevski * Fixed delineation of 200 status message in response to CONNECT method Contributed by Oleg Kalnichevski * HTTPCORE-468: Allow HttpAsyncService subclasses to customize the HTTP status code. Contributed by Gary Gregory * HTTPCORE-471: Add APIs URIBuilder.localhost() and setHost(InetAddress) Contributed by Gary Gregory * HTTPCORE-466: Round out the SslContextBuilder by adding missing APIs. Contributed by Gary Gregory Release 5.0-ALPHA3 ------------------- This is a major release that renders HttpCore API incompatible with the stable 4.x branch and upgrades HTTP/1.1 and HTTP/2 protocol conformance to the requirements and recommendations of the latest protocol specification. Notable changes and features included in the 5.0 series are: * Partial support for HTTP/2 protocol and conformance to requirements and recommendations of the latest HTTP/2 protocol specification (RFC 7540, RFC 7541) Supported features: ** HPACK header compression ** stream multiplexing (client and server) ** flow control ** response push (client and server) ** message trailers ** expect-continue handshake ** connection validation (ping) ** application-layer protocol negotiation (ALPN) on Java 1.9+ ** TLS 1.2 security features Unsupported features: ** padding of outgoing frames ** stream priority ** plain connection HTTP/1.1 upgrade ** CONNECT method * Improved conformance to requirements and recommendations of the latest HTTP/1.1 protocol specification (RFC 7230, RFC 7231) * New asynchronous HTTP transport APIs consistent for both HTTP/1.1 and HTTP/2 transport. * Improved HTTP/1.1 and HTTP/2 requester and server implementations. * Redesigned connection pool implementation with reduced pool lock contention. * Plug-in mechanism for HTTP/1.1 protocol switch / upgrade. * Package name space changed to 'org.apache.hc.core5' * Maven group id changed to 'org.apache.httpcomponents.core5' HttpCore 5.0 releases can be co-located with earlier versions. Please note that as of 5.0 HttpCore requires Java 1.7 or newer. Please note that at this point 5.0 APIs are considered API experimental and unstable and are expected to change in the coming releases without providing a migration path. Change Log ------------------- * Support for HTTP protocol version negotiation. Contributed by Oleg Kalnichevski * Support TLS ALPN and disable TLS renegotiation via reflection on Java 1.9+. Contributed by Oleg Kalnichevski * HTTPCORE-452: Add a UriRegexMatcher. Contributed by Gary Gregory * HTTPCORE-453: ServerBootstrap should traverse handler map in insertion order. Contributed by Gary Gregory * HTTPCORE-451: Add a TimeValue class to wrap a long and a TimeUnit. Contributed by Gary Gregory * Fixed HTTP/2 server-side response message delineation when replying to HEAD requests. Contributed by Oleg Kalnichevski * Rewrite of non-blocking HTTP/1.1 connection persistence and re-use code. Contributed by Oleg Kalnichevski * API for graceful shutdown of processes or endpoints Contributed by Oleg Kalnichevski * HTTPCORE-450: Add a Provider parameter in SSLContextBuilder. Contributed by lujianbo <387852424 at qq dot com>, Gary Gregory * Improved handling of premature HTTP/1.1 stream termination by non-blocking protocol handlers. Contributed by Oleg Kalnichevski * Non-blocking connection initializer to support multi-homed remote endpoints. Contributed by Oleg Kalnichevski * HTTPCORE-458: Validate port values Contributed by Gary Gregory Release 5.0-ALPHA2 ------------------- This is a major release that renders HttpCore API incompatible with the stable 4.x branch and upgrades HTTP/1.1 and HTTP/2 protocol conformance to the requirements and recommendations of the latest protocol specification. Notable changes and features included in the 5.0 series are: * Partial support for HTTP/2 protocol and partial conformance to requirements and recommendations of the latest HTTP/2 protocol specification (RFC 7540, RFC 7541) Supported features: ** HPACK header compression ** stream multiplexing (client and server) ** flow control ** response push (client and server) ** message trailers ** expect-continue handshake ** connection validation (ping) ** TLS 1.2 features Unsupported features: ** application-layer protocol negotiation (ALPN) ** padding of outgoing frames ** stream priority ** plain connection HTTP/1.1 upgrade ** CONNECT method ** TLS renegotiation and compression cannot be disabled with Java 1.7 JSSE APIs * Improved conformance to requirements and recommendations of the latest HTTP/1.1 protocol specification (RFC 7230, RFC 7231) * New asynchronous HTTP transport APIs consistent for both HTTP/1.1 and HTTP/2 transport. * Improved HTTP/1.1 and HTTP/2 requester and server implementations. * Redesigned connection pool implementation with reduced pool lock contention. * Support for HTTP/1.1 protocol switch / upgrade. * Package name space changed to 'org.apache.hc.core5' * Maven group id changed to 'org.apache.httpcomponents.core5' HttpCore 5.0 releases can be co-located with earlier versions. Please note that as of 5.0 HttpCore requires Java 1.7 or newer. Please note that at this point 5.0 APIs are considered API experimental and unstable and are expected to change in the coming releases without providing a migration path. Change Log ------------------- * HTTPCORE-424: added ConnPool policy parameter to control connection re-use policy. Contributed by Oleg Kalnichevski * HTTPCORE-413: Minimal chunk side can now be specified as H1Config#chunkSizeHint. The value is treated as a hint. Both classic and NIO attempt to apply it when sending / receiving messages without providing a strict guarantee. Contributed by Oleg Kalnichevski * HTTPCORE-429: NIO connection pool incorrectly reports the number of pending connections per individual route. Contributed by Oleg Kalnichevski * HTTPCORE-432: HTTP/2 support. Contributed by Oleg Kalnichevski * HTTPCORE-418: Add a HttpHost constructor for hostname and scheme. Based on contribution by Joshua Hendrickson * HTTPCORE-436: Port tests from Apache Commons Logging to Apache Log4j 2. Contributed by Gary Gregory * HTTPCORE-434: Corrected handling of HEAD responses without payload headers. Contributed by Oleg Kalnichevski * HTTPCORE-427: SSL I/O sessions to use a finite timeout for SSL shutdown handshake when closing. Contributed by Oleg Kalnichevski * HTTPCORE-422: HttpAsyncResponseConsumer#onEntityEnclosed is triggered for HEAD responses. Contributed by Oleg Kalnichevski * HTTPCORE-420: Blocking HttpServer does not close out persistent connection when shut down. Contributed by Oleg Kalnichevski * HTTPCORE-417: SSLIOSession#writePlain incorrectly handles closed channel condition leading to an infinite loop. Contributed by Oleg Kalnichevski * HTTPCORE-416: DefaultConnectingIOReactor to treat failure to open a channel for a new outgoing connection as a recoverable request failure. Contributed by Oleg Kalnichevski Release 5.0-ALPHA1 ------------------- This is a major release that renders HttpCore API incompatible with the stable 4.x branch and upgrades HTTP/1.1 protocol conformance to the requirements and recommendations of the latest protocol specification. This release lays the foundation for transition to HTTP/2 as the primary transport protocol in the future releases. Notable changes and features included in the 5.0 series are: * Improved conformance to requirements and recommendations of the latest HTTP/1.1 protocol specification (RFC 7230, RFC 7231) * Blocking I/O and NIO HTTP transport implementation has been folded into one module * Package name space changed to 'org.apache.hc.core5' * Maven group id changed to 'org.apache.httpcomponents.core5' HttpCore 5.0 releases can be co-located with earlier versions. Please note that as of 5.0 HttpCore requires Java 1.7 or newer. Please note that at this point 5.0 APIs are considered API experimental and unstable and are expected to change in the coming releases without providing a migration path. Change Log ------------------- * PATCH method support Contributed by Ömer Özkan * HTTPCORE-412: Support for trailing headers in outgoing HTTP messages Based on contribution by Daneel Yaitskov * HTTPCORE-411, RFC 7320, RFC 7321: Made Expect-Continue handshake compliant with the spec Contributed by Oleg Kalnichevski * Fixed handling of pipelined HEAD requests Contributed by Oleg Kalnichevski * HTTPCORE-409: NIO HttpServer does not shutdown listener ExecutorService Contributed by Hiranya Jayathilaka * HTTPCORE-406: PAX-EXAM tests with an embedded HTTP server Contributed by Benson Margulies * RFC 7230: increased the default max number of concurrent connection for the same route from 2 to 5 Contributed by Oleg Kalnichevski * RFC 7230: improved compliance of the default connection re-use strategy Contributed by Oleg Kalnichevski * RFC 7230: reject HTTP/1.1 requests with absent Host header or multiple Host headers Contributed by Oleg Kalnichevski * RFC 7230: permit some empty lines before message head Contributed by Oleg Kalnichevski * RFC 7230: disallow multiple Content-Length headers / header elements Contributed by Oleg Kalnichevski * RFC 7230: reject headers containing whitespaces between the header field name and colon in strict mode (when parsing request messages on the server-side) Contributed by Oleg Kalnichevski * RFC 7230: reject messages with incompatible major protocol version Contributed by Oleg Kalnichevski * HTTP/2: added immutable MessageHead interface common for both HTTP/1.1 and HTTP/2 messages Contributed by Oleg Kalnichevski * HTTPCORE-396: PrivateKeyStrategy does not work with NIO SSL Contributed by Oleg Kalnichevski * RFC 7230: it is legal for any request method to enclose an entity; revised message delineation logic; removed HttpEntityEnclosingRequest interface; fixes HTTPCORE-318, HTTPCORE-380 Contributed by Oleg Kalnichevski * Use CharSequence instead of CharArrayBuffer for header element parsing Contributed by Oleg Kalnichevski * TokenParser to use CharSequence instead of CharArrayBuffer Contributed by Oleg Kalnichevski Release 4.4.4 ------------------- This is a maintenance release that fixes a number of issues discovered since 4.4.3. Please note that as of 4.4 HttpCore requires Java 1.6 or newer. Change Log ------------------- * [HTTPCORE-410] PoolStats made Serializable Contributed by Oleg Kalnichevski * BufferedHttpEntity to use HttpEntity#writeTo when buffering non-repeatable entities Contributed by Oleg Kalnichevski * Fixed race condition in request initialization code in async client protocol handlers Contributed by Oleg Kalnichevski * Fixed handling of pipelined HEAD requests Contributed by Oleg Kalnichevski * [HTTPCORE-409] NIO HttpServer does not shutdown listener ExecutorService Contributed by Hiranya Jayathilaka Release 4.4.3 ------------------- This is a maintenance release that fixes a regression introduced by release 4.4.2. Please note that as of 4.4 HttpCore requires Java 1.6 or newer. Change Log ------------------- * Fixed regression introduced by HTTPCORE-399. Contributed by Oleg Kalnichevski Release 4.4.2 ------------------- This maintenance release fixes a bug in HTTP request pipelining code discovered after 4.4.1 release. Please note that as of 4.4 HttpCore requires Java 1.6 or newer. Change Log ------------------- * Throw ConnectionClosedException instead of IllegalStateException if an attempt is made to use a closed (not bound to a socket) blocking connection. Contributed by Oleg Kalnichevski * [HTTPCORE-399] Non-blocking client connections incorrectly suspend output causing sequential execution of requests. Contributed by Oleg Kalnichevski Release 4.4.1 ------------------- This maintenance release fixes a number of minor bugs found since 4.4. Please note that as of 4.4 HttpCore requires Java 1.6 or newer. Change Log ------------------- * [HTTPCORE-396]: PrivateKeyStrategy does not work with NIO SSL. Contributed by Oleg Kalnichevski * [HTTPCORE-395]: VersionInfo#getUserAgent reports incorrect Java version. Contributed by Michael Osipov * Non-blocking connection should not trigger end-of-stream callback as long as there is still data in the session input buffer. This can cause a series of short pipelined requests to fail prematurely in case of an unexpected connection termination by the opposite endpoint. Contributed by Oleg Kalnichevski Release 4.4 ----------------- This is the first stable (GA) release of HttpCore 4.4. Notable features included in the 4.4 series are: * Support for pipelined request processing on the server-side * Support for pipelined request execution on the client side * Simplified bootstrapping of blocking and non-blocking (NIO) HTTP server implementations * Inclusion of SSL context initialization utilities from HttpClient Please note that as of 4.4 HttpCore requires Java 1.6 or newer. Change Log ------------------- * Performance optimizations Contributed by Dmitry Potapov * Update Apache Commons Logging version from 1.1.3 to 1.2. Contributed by Gary Gregory Release 4.4-BETA1 ------------------- This is the first BETA release from 4.4 release series. Notable features included in the 4.4 series are: * Support for pipelined request processing on the server-side * Support for pipelined request execution on the client side * Simplified bootstrapping of blocking and non-blocking (NIO) HTTP server implementations * Inclusion of SSL context initialization utilities from HttpClient * New HTTP element tokenizer implementation Please note that as of 4.4 HttpCore requires Java 1.6 or newer. Release 4.4-ALPHA1 ------------------- This is the first release from the 4.4.x development branch. The most notable features included in this release are: * Support for pipelined request processing on the server-side * Support for pipelined request execution on the client side * Simplified bootstrapping of blocking and non-blocking (NIO) HTTP server implementations This release also includes all fixes from the stable 4.2.x release branch. Change Log ------------------- * [HTTPASYNC-77] system resources are not correctly deallocated if I/O reactor is shut down while still inactive (not started) Contributed by Oleg Kalnichevski * [HTTPCORE-377] Allow zero SO_LINGER (immediate abortive close). Contributed by Dmitry Potapov * [HTTPCORE-376] AbstractNIOConnPool#requestCancelled() should not process pending requests wnen being shut down. Contributed by Dmitry Potapov * [HTTPASYNC-69]: async request handler is closed by HttpAsyncRequestExecutor#closed if the underlying connection is found to be in an inconsistent state. Contributed by Oleg Kalnichevski * [HTTPCORE-372] Blocking and non-blocking chunk decoders to throw an I/O exception if data stream is terminated without a closing chunk. Contributed by Dmitry Potapov * [HTTPCORE-368] Customizable buffer management strategies for SSLIOSession. Contributed by offbynull * [HTTPCORE-358] Added I/O reactor listener backlog parameter. Contributed by Dmitry Potapov * [HTTPCORE-357] Avoid DNS lookups in SSLIOSession. Contributed by Oleg Kalnichevski * Update JUnit to version 4.11 from 4.9 Contributed by Gary Gregory Release 4.3.2 ------------------- This maintenance release fixes a number of bugs and regressions found since 4.3.1, mostly in the NIO transport components. All users of HttpCore 4.3 are advised to upgrade. Change Log ------------------- * [HTTPCORE-371] Support for SSL re-negotiation with NIO. Contributed by Asankha Perera * [HTTPCORE-373] Out of sequence HTTP response causes NPE in HttpAsyncRequestExecutor. Contributed by Oleg Kalnichevski * [HTTPCORE-370] Race condition if connection request succeeds and times out at the same time. Contributed by Oleg Kalnichevski * (Regression) Fixed synchronization issue in blocking and non-blocking connection pool implementations caused by HTTPCORE-362 Contributed by Oleg Kalnichevski Release 4.3.1 ------------------- This maintenance release fixes a number of bugs and regressions found since 4.3, mostly in the NIO transport components. All users of HttpCore 4.3 are advised to upgrade. Change Log ------------------- * [HTTPCORE-367] (Regression) Non-blocking connections can enter a tight loop while waiting for a chunk header split across multiple TCP frames. Contributed by Oleg Kalnichevski * [HTTPCORE-366] Non-blocking SSLIOSession can enter an infinite loop if the underlying channel receives incoming data simultaneously with inactivity timeout. Contributed by Oleg Kalnichevski * [HTTPCORE-364] IOSessionImpl.getLocalAddress() (etc.) creates unnecessary copy of channel * DefaultConnectingIOReactor / DefaultListeningIOReactor do not correctly apply some initial socket settings. Contributed by Andreas Veithen * [HTTPCORE-357] Avoid DNS lookups in SSLIOSessions in server mode. Contributed by Isaac Cruz Ballesteros * [HTTPCORE-362] Purge pool per route map after closing out expired or idle connections with #closeExpired and #closeIdle methods. Contributed by Oleg Kalnichevski * [HTTPCORE-361] Reduced intermediate garbage in HeaderGroup#getFirstHeader() Contributed by Oleg Kalnichevski * [HTTPCORE-355] HttpAsyncRequestExecutor fails when its handler is not ready to generate a request. Contributed by jd * [HTTPCORE-354] BasicConnFactory don't use SocketConfig#isSoKeepAlive. Contributed by David Ignjic * [HTTPCORE-347] (Regression) HttpResponse#setStatusCode() does not update reason phrase. Contributed by Oleg Kalnichevski Release 4.3 ------------------- This is the first stable (GA) release of HttpCore 4.3. The most notable features in the 4.3 branch are: * Deprecation of preference and configuration API based on HttpParams interface in favor of constructor injection and plain configuration objects. * Reliance on object immutability instead of access synchronization for thread safety. Several old classes whose instances can be shared by multiple request exchanges have been replaced by immutable equivalents. The 4.3 branch also contains performance optimizations such as reduced TCP packet fragmentation and more efficient lease / release operations for pools of persistent connections on the client side. This release also includes all fixes from the 4.2.x release branch. Users of HttpCore 4.2 are encouraged to upgrade. Change Log ------------------- * [HTTPCORE-343] AbstractNIOConnPool to fire request callbacks outside the pool lock. This makes it possible to re-enter pool methods from a callback event. Contributed by Oleg Kalnichevski * [HTTPCORE-340] AbstractNIOConnPool to support lease timeout distinct from connect timeout. Contributed by Ignat Alexeyenko * Blocking connections do not clean session input buffer when closed (input data from a read operation may still remain in the buffer if the connection is re-opened). Contributed by Oleg Kalnichevski Release 4.2.5 ------------------- This maintenance release fixes a number of bugs found in NIO components since 4.2.4. We advise users of HttpCore NIO of all versions to upgrade. This is likely to be the last release in the 4.2.x branch. Change Log ------------------- * [HTTPCORE-345] EntityAsyncContentProducer fails to release resources allocated by the underlying entity when #produceContent is never invoked. Contributed by Tad Whitenight * Non-blocking connection pool to avoid scanning the entire queue of pending connection requests on each connection lease / release operation (under heavy load the request queue can contain a significant number of pending requests, a full linear scan of which can cause massive performance degradation). Contributed by Oleg Kalnichevski * Use bulk ByteBuffer#put method instead of single byte ByteBuffer#put Contributed by Oleg Kalnichevski * [HTTPCORE-336] Unhandled CancelledKeyException leads to a shutdown of the underlying IOReactor. Contributed by Thomas Dudek Release 4.3-BETA2 ------------------- This is the second BETA release from the 4.3.x release branch. This release addresses performance issues in the non-blocking connection pool implementation and also includes a number of performance improvements in the low level NIO based transport components. Change Log ------------------- * [HTTPCORE-300] ContentType to provide support for custom parameters. Contributed by Oleg Kalnichevski * Non-blocking connection pool to avoid scanning the entire queue of pending connection requests on each connection lease / release operation (under heavy load the request queue can contain a significant number of pending requests, a full linear scan of which can cause massive performance degradation). Contributed by Oleg Kalnichevski * Basic connection pool implementations to perform default port resolution for HTTP and HTTPS schemes. Contributed by Oleg Kalnichevski * Use bulk ByteBuffer#put method instead of single byte ByteBuffer#put Contributed by Oleg Kalnichevski * [HTTPCORE-336] Unhandled CancelledKeyException leads to a shutdown of the underlying IOReactor. Contributed by Thomas Dudek Release 4.3-BETA1 ------------------- This is the first BETA release from the 4.3.x release branch. The main theme of the 4.3 release series is streamlining of component configuration and deprecation of the old configuration API based on HttpParams in favor of constructor-based dependency injection and plain objects for configuration parameters. This release also includes performance optimizations intended to reduce TCP packet fragmentation when writing out HTTP messages both in blocking and non-blocking I/O modes, which should result in up to 20% higher throughput for short entity enclosing messages. This release also includes all fixes from the stable 4.2.x release branch. Change Log ------------------- * Reduced TCP packet fragmentation when writing out message content with blocking and non-blocking connections. Contributed by Oleg Kalnichevski * [HTTPCORE-330] Clarify InputStreamEntity length constructor argument. Contributed by John McCarthy * [HTTPCORE-323] Undocumented UnsupportedCharsetException in ContentType#getOrDefault. Contributed by Gary D. Gregory Release 4.2.4 ------------------- This maintenance release fixes a number of bugs found in NIO components since 4.2.3. We advise users of HttpCore NIO of all versions to upgrade. Change Log ------------------- * [HTTPCORE-334] https request to a non-responsive but alive port over a non-blocking connection results in a busy loop in one of I/O dispatch threads. Contributed by Scott Stanton * [HTTPCORE-331] BasicFuture no longer executes notification callbacks inside a synchronized block. Contributed by Oleg Kalnichevski * [HTTPCORE-319] Non-blocking SSLIOSession can fail to shut down correctly when the underlying connection gets terminated abnormally by the opposite endpoint in case there is a truncated or corrupted encrypted content in the input buffer and there is still data in the output buffer that needs to be flushed out (most likely to occur with POST or PUT requests). Contributed by Oleg Kalnichevski Release 4.3-ALPHA1 ------------------- This is the first release from the 4.3.x release branch. The main focus of the 4.3 release series is streamlining of component configuration and deprecation of the old configuration API based on HttpParams in favor of constructor based dependency injection and plain objects for configuration parameters. We are kindly asking all upstream projects to review API changes and help us improve the APIs by providing feedback and sharing ideas on dev@hc.apache.org. This release also includes all fixes from the stable 4.2.x release branch. Release 4.2.3 ------------------- This maintenance release fixes a number of bugs and found since 4.2.2 including a major bug in the NIO module that can cause an infinite loop in SSL sessions under special circumstances when the remote peer terminates the session in the middle of SSL handshake. Please note this issue does not affect blocking I/O components used by HttpClient. We advise users of HttpCore NIO of all versions to upgrade. Change Log ------------------- * [HTTPCORE-319, HTTPCORE-322] Non-blocking SSLIOSession can enter an infinite loop under special circumstances when the remote peer terminates the session in the middle of SSL handshake. Contributed by Paul Donohue and Oleg Kalnichevski * [HTTPCORE-316] HeaderGroup#clone removes headers from the original object. Contributed by Markus Thies * [HTTPCORE-315] AbstractNIOConnPool fails to correctly deallocate connection if it is immediately released from the session request callback causing a resource leak. Contributed by Scott Stanton * [HTTPCORE-313] ContentType#parse now ignores empty and blank charset attributes. HttpEntityUtils#toString now throws checked I/O exception if it encounters an unsupported charset. Contributed by Oleg Kalnichevski Release 4.2.2 ------------------- This is a maintenance release that fixes a number of bugs and regressions found since 4.2.1 including a major bug in the NIO module causing incorrect handling of outgoing Content-Length delimited messages larger than 2GB. Users of HttpCore 4.2 are advised to upgrade. Change Log ------------------- * [HTTPCORE-312] NIO length delimited content encoder incorrectly handles messages larger than 2GB. Contributed by Oleg Kalnichevski * [HTTPCORE-310] Fixed regression in DefaultConnectionReuseStrategy causing it to incorrectly flag connections as non-reusable after a 204, 205 or 304 response. Contributed by Oleg Kalnichevski * [HTTPCORE-309] Fixed regression in HttpAsyncRequestExecutor causing it to handle 204, 205 and 304 responses incorrectly by returning a message with an enclosed content body. Contributed by Oleg Kalnichevski * [HTTPCORE-306] I/O reactor TCP_NODELAY parameter now defaults to true. Contributed by Oleg Kalnichevski * [HTTPASYNC-21] request execution handler does not get closed in case of a premature HTTP exchange termination. Contributed by Oleg Kalnichevski * [HTTPCORE-303] ContentType made Serializable. Contributed by Oleg Kalnichevski Release 4.2.1 ------------------- This is a maintenance release that fixes a non-critical number of bugs found since 4.2. Users of HttpCore 4.2 are encouraged to upgrade. Change Log ------------------- * [HTTPCORE-299] ContentType should rely on Charset#name() instead of Charset#toString() for building header value. Contributed by Oleg Kalnichevski * [HTTPCORE-263] Under rare circumstances incorrectly delineated or garbled HTTP message can cause an IndexOutOfBoundsException in AbstractSessionInputBuffer#readLine() Contributed by Michael Pujos * Made connection pools use FIFO algorithm instead of LIFO when leasing / releasing persistent connections. Contributed by Oleg Kalnichevski * [HTTPCORE-298] Fixed non-blocking SSLIOSession state can getting out of sync with the underlying IOSession in case the I/O session is terminated by the I/O reactor rather than by the protocol handler. Contributed by Sandeep Tamhankar * Fixed NPE in StringEntity constructor thrown if ContentType#getCharset is null. Contributed by Oleg Kalnichevski Release 4.2 ------------------- This is the first stable (GA) release of HttpCore 4.2. The most notable features included in this release are connection pool components for blocking and non-blocking HTTP connections and new asynchronous client and server-side protocol handlers. New protocol handling API used in conjunction with connection pooling components is expected to make development of asynchronous HTTP client agents and HTTP proxies easier and less error prone. Connection pool components are based on mature code migrated from HttpClient and HttpAsyncClient modules but have a slightly different API that makes a better use of Java standard concurrent primitives. Change Log ------------------- * Fixed HttpAsyncRequestExecutor incorrect execution of message exchanges that span across multiple hosts (for instance, in case of a request redirect). Contributed by Oleg Kalnichevski * AbstractHttpClientConnection#isResponseAvailable method now catches SocketTimeoutException and returns false. Contributed by Oleg Kalnichevski * [HTTPCORE-296] server-side connections (both blocking and non-blocking) can now handle entity enclosing requests without Content-Length and Transfer-Encoding headers. Contributed by Oleg Kalnichevski * [HTTPCORE-295] [HTTPCORE-288] Provided direct access to the underlying socket of non-blocking HTTP connection to allow modification of socket level settings such as TCP_NODELAY, SO_KEEPALIVE, TrafficClass, etc. Contributed by Oleg Kalnichevski * [HTTPCORE-289] HttpAsyncService fails to invoke Cancellable#cancel() when the ongoing HTTP exchange is aborted by the client. Contributed by Oleg Kalnichevski Incompatible changes -------------------- [Compared to release version 4.1.4] The following methods have been deprecated for some time now and have been deleted: org.apache.http.impl.SocketHttpServerConnection#createHttpDataReceiver(Socket, int, HttpParams) org.apache.http.impl.SocketHttpServerConnection#createHttpDataTransmitter(Socket, int, HttpParams) org.apache.http.protocol.HttpRequestHandlerRegistry#matchUriRequestPattern(String, String) The following classes have been deprecated for some while now and have been deleted: org.apache.http.nio.entity.ByteArrayNIOEntity org.apache.http.nio.entity.FileNIOEntity org.apache.http.nio.entity.HttpNIOEntity org.apache.http.nio.entity.StringNIOEntity org.apache.http.nio.protocol.NHttpClientHandlerBase org.apache.http.nio.protocol.NHttpServiceHandlerBase Release 4.2-BETA1 ------------------- This is the first BETA release of HttpCore 4.2. This release comes with completely redesigned and rewritten asynchronous protocol handlers. New protocol handling API used in conjunction with connection pooling components is expected to make development of asynchronous HTTP client agents and HTTP proxies easier and less error prone. Sample application shipped with the release include an example of an HTTP file server capable of direct channel (zero copy) data transfer and an example of a non-blocking, fully streaming reverse proxy. This release also incorporates bug fixes from the stable 4.1.x branch and includes an updated HttpCore tutorial. Release 4.1.4 ------------------- This is a maintenance release that fixes a number of bugs found since 4.1.3. It is also likely to be the last release in the 4.1.x branch. Change Log ------------------- * [HTTPCORE-286] Canceled I/O session can cause an IllegalStateException in BaseIOReactor#validate leading to an abnormal termination of the I/O reactor. Contributed by Oleg Kalnichevski * [HTTPCORE-257] Fixed incorrect results produced by DefaultConnectionReuseStrategy when handling response messages whose content entity has been decoded or modified by a protocol interceptor. Contributed by Oleg Kalnichevski * [HTTPCORE-283] Workaround for a bug causing termination of the I/O reactor in case of exception thrown by NHttpServiceHandler#requestReceived or NHttpClientHandler#responseReceived methods. A more comprehensive fix for the bug applied to the 4.2 branch. Contributed by Oleg Kalnichevski * [HTTPCORE-281] ResponseConnControl protocol interceptor does not correctly populate connection persistence control headers when sending a HTTP/1.1 response message in response to a HTTP/1.0 request message. Contributed by William R. Speirs * [HTTPCORE-282] The default value of the internal event mask of newly created non-blocking I/O is not correctly initialized, which causes the READ interest bit to get cleared in the interest op queuing mode unless the event mask is explicitly reset. Contributed by Sadeep Jayasumana and Oleg Kalnichevski * [HTTPCORE-268] Handle runtime exceptions thrown by SSLEngine. Contributed by Oleg Kalnichevski Release 4.2-ALPHA2 ------------------- This is the second ALPHA release of HttpCore 4.2. This release comes with completely redesigned and rewritten asynchronous protocol handlers. New protocol handling API used in conjunction with connection pooling components introduced in the previous ALPHA release is expected to make development of asynchronous HTTP client agents and HTTP proxies easier and less error prone. Sample application shipped with the release include an example of an HTTP file server capable of direct channel (zero copy) data transfer and an example of a non-blocking, fully streaming reverse proxy. We are kindly asking existing and prospective users of HttpCore to review and try out the new protocol handlers and give us feedback while the 4.2 API is still not final. If no major flaws are discovered the 4.2 API is expected to be frozen with the next BETA release. Please note that new features included in this release are still considered experimental and their API may change in the future ALPHA releases. This release also marks the end of support for Java 1.3. As of this release HttpCore requires Java 1.5 for all its components. Several classes and methods deprecated between versions 4.0-beta1 and 4.0 GA (more than two years ago) have been removed in this release. Change Log ------------------- * [HTTPCORE-270] Fixed IllegalStateException in AbstractSessionOutputBuffer and AbstractSessionInputBuffer. Contributed by William R. Speirs * [HTTPCORE-269] Connection pools incorrectly handle lease requests when the max limit for the given route has been exceeded and all connections in the route pool are stateful. Contributed by Oleg Kalnichevski Release 4.2-ALPHA1 ------------------- This is the first ALPHA release of 4.2. The most notable feature included in this release is support for connection pools of blocking and non-blocking HTTP connections. Connection pool components are based on mature code migrated from HttpClient and HttpAsyncClient modules but have a slightly different API that makes a better use of Java standard concurrent primitives. Support for connection pools in HttpCore is expected to make development of client and proxy HTTP services easier and less error prone. Please note that new features included in this release are still considered experimental and their API may change in the future ALPHA releases. This release also marks the end of support for Java 1.3. As of this release HttpCore requires Java 1.5 for all its components. Several classes and methods deprecated between versions 4.0-beta1 and 4.0 GA (more than two years ago) have been removed in this release. Change Log ------------------- * [HTTPCORE-268] Handle runtime exceptions thrown by SSLEngine. Contributed by Oleg Kalnichevski Release 4.1.3 ------------------- This is an emergency release that fixes a severe regression in the non-blocking SSL I/O code introduced in release 4.1.2. * [HTTPCORE-266] SSLIOSession does not correctly terminate if the opposite end shuts down connection without sending a 'close notify' message causing an infinite loop in the I/O dispatch thread. Contributed by Oleg Kalnichevski Release 4.1.2 ------------------- This is a patch release that fixes a number of bugs found in the previous version. Please note that several classes and methods deprecated between versions 4.0-beta1 and 4.0 GA (more than two years ago) will also be removed in the 4.2 branch. Users of 4.0.x versions are advised to upgrade and replace deprecated API calls following recommendations in Javadocs. * [HTTPCORE-261] IOSession#setSocketTimeout() method does not reset the timeout count. Contributed by Oleg Kalnichevski * [HTTPCORE-260] Non-blocking SSL I/O session can terminate prematurely causing message body truncation when message content is chunk coded and the connection is closed on the opposite end. Contributed by Oleg Kalnichevski * [HTTPCORE-257] Fixed incorrect results produced by DefaultConnectionReuseStrategy when handling response messages whose content entity has been decoded or modified by a protocol interceptor. Contributed by Oleg Kalnichevski Release 4.1.1 ------------------- This is a patch release that fixes a number of non-critical issues found since release 4.1. This release marks the end of support for Java 1.3. As of release 4.2 HttpCore will require Java 1.5 for all its components. Please note that several classes and methods deprecated between versions 4.0-beta1 and 4.0 GA (more than two years ago) will also be removed in the 4.2 branch. Users of 4.0.x versions are advised to upgrade and replace deprecated API calls following recommendations in Javadocs. * In case of an unexpected end of stream condition (the peer closed connection prematurely) truncated Content-Length delimited message bodies will cause an I/O exception. Application can still choose to catch and ignore ConnectionClosedException in order to accept partial message content. Contributed by Oleg Kalnichevski * [HTTPCORE-255]: Fixed resource management in InputStreamEntity#writeTo() Contributed by Oleg Kalnichevski * [HTTPCORE-254]: Erratic results from metrics (sebb) * [HTTPCORE-242]: Fixed NPE in AsyncNHttpClientHandler caused by an early response to an entity enclosing request. Contributed by Oleg Kalnichevski Release 4.1 ------------------- This is the first stable (GA) release of HttpCore 4.1. This release provides a compatibility mode with JREs that have a naive (broken) implementation of SelectionKey API and also improves compatibility with the Google Android platform. There has also been a number of performance related improvements and bug fixes in both blocking and non-blocking components. Change Log ------------------- * [HTTPCORE-240]: DefaultConnectingIOReactor leaks a socket descriptor if the session request fails. Contributed by Oleg Kalnichevski * [HTTPCORE-239]: The ChunkEncoder could request for a negative buffer limit causing an IllegalArgumentException. Contributed by Asankha Perera * [HTTPCORE-236]: SSLIOSession#isAppInputReady() does not check the status of the session input buffer. Contributed by Dmitry Lukyanov * [HTTPCORE-233]: EntityUtils#toString() and EntityUtils#toByteArray() to return null if HttpEntity#getContent() is null Contributed by Oleg Kalnichevski * [HTTPCORE-231] Fixed incorrect handling of HTTP entities by non-blocking LengthDelimitedDecoder when the Content-Length value is larger than Integer.MAX_VALUE. Contributed by Oleg Kalnichevski Release 4.1-BETA2 ------------------- This is the second BETA release of HttpCore 4.1. This is mainly a bug fix release that addresses a number of non-critical bugs. The most significant change in this release is deprecation of the HttpEntity#consumeContent() method and streamlining of connection management and resource deallocation by HTTP entities. Please refer to the Javadocs for details. * [HTTPCORE-229] AbstractSessionInputBuffer#readLine(CharArrayBuffer) returns incorrect number of characters read by the method when using non-standard HTTP element charset. Contributed by Oleg Kalnichevski * Non-blocking connections can trigger #responseReady / #requestReady events by mistake when the underlying session is already closed. Contributed by Oleg Kalnichevski * [HTTPCORE-228] Fixed NPE in AsyncNHttpServiceHandler caused by entity enclosing requests if no matching request handler can be found. Contributed by Oleg Kalnichevski * [HTTPCORE-227] Fixed incorrect request / response count by non-blocking connections. Contributed by Harold Lee * [HTTPCORE-226] Improved compatibility of NIO components with Google Android. Contributed by Oleg Kalnichevski * ByteArrayBuffer, CharArrayBuffer, BasicHeader, BufferedHeader, HeaderGroup, BasicRequestLine, BasicStatusLine made Serializable. Contributed by Oleg Kalnichevski Release 4.1-BETA1 ------------------- This is the first BETA release of HttpCore 4.1. This release finalizes the API introduced in the 4.1 development branch. It also fixes a number of bugs discovered since the previous release and delivers a number of performance optimizations in the blocking HTTP transport components. The blocking HTTP transport is expected to be 5% to 10% faster compared to previous releases. * [HTTPCORE-222] Fixed Import-Package in the OSGi META-INF Contributed by Willem Jiang * [HTTPCORE-177] Reduce intermediate data buffering by reading large chunks of data directly from the underlying socket stream. This results in improved performance of blocking read operations. Contributed by Oleg Kalnichevski * [HTTPCORE-220] IdentityDecoder fails to detect end of stream when using file channels. Contributed by Asankha C. Perera * [HTTPCORE-218] ChunkEncoder#write method no longer returns incorrect value if the data to write is greater than the size of the internal buffer used by the encoder. Contributed by Richie Jefts * [HTTPCORE-209] Added parameter to set SO_REUSEADDR on sockets bound to a local address. Contributed by Oleg Kalnichevski * [HTTPCORE-207] SocketHttp*Connection classes can leak sockets if the connection is half-closed Contributed by David Koski Release 4.1-ALPHA1 ------------------- This is the first public release from the 4.1 branch of HttpCore. This release adds a number of new features, most notable being introduction of compatibility mode with IBM JREs and other JREs with naive (broken) implementation of SelectionKey API. Please note new classes and methods added in the 4.1 branch are still considered API unstable. * Ensure that an attempt is made to close out all active sessions gracefully in case of an abnormal shutdown of the I/O reactor. Contributed by Oleg Kalnichevski * [HTTPCORE-201] OSGi Export-Package to specify release version Contributed by Oleg Kalnichevski * [HTTPCORE-183] Added Thread-safe implementations of HttpParams and HttpProcessor - SyncBasicHttpParams and ImmutableHttpProcessor classes Contributed by Oleg Kalnichevski * [HTTPCORE-199] ContentInputStream implements InputStream#available(). Contributed by Oleg Kalnichevski * [HTTPCORE-195] Truncated chunk-coded streams can now be tolerated by catching and discarding TruncatedChunkException. Contributed by Oleg Kalnichevski * [HTTPCORE-155] Compatibility mode with IBM JRE and other JREs with naive (broken) implementation of SelectionKey. Contributed by Marc Beyerle and Oleg Kalnichevski * [HTTPCORE-191] Blocking HTTP connections are now capable of correctly preserving their internal state on SocketTimeoutExceptions, which makes it possible to continue reading from the connection after a socket timeout. Contributed by Oleg Kalnichevski * [HTTPCORE-190] ChunkedInputStream is now capable of correctly preserving its internal state on SocketTimeoutExceptions, which makes it possible to continue reading from the stream after a socket timeout. Contributed by Oleg Kalnichevski Release 4.0.1 ------------------- This is a patch release addressing a number of issues discovered since the 4.0 release. Users of NIO module are advised to upgrade. * [HTTPCORE-198] CONNECT request includes Host header for HTTP 1.1 connections. Contributed by Oleg Kalnichevski * [HTTPCORE-196] SSLIOSession now unwraps encrypted data more aggressively eliminating long pauses when receiving data over non-blocking connections. Contributed by Oleg Kalnichevski * [HTTPCORE-197] Fixed bug causing the non-blocking ChunkDecoder to report some data stream as truncated under special conditions. Contributed by Denis Rogov and Oleg Kalnichevski * SSLIOSession#isAppOutputReady and SSLIOSession#isAppInputReady no longer ignore the application event mask causing I/O event notifications for unrequested type of events. Contributed by Oleg Kalnichevski * [HTTPCORE-193] Fixed problem with SSLIOSession incorrectly handling of end-of-stream condition. Contributed by Asankha C. Perera and Oleg Kalnichevski Release 4.0 ------------------- This is the first stable (GA) release of HttpCore 4.0. This release mainly improves the documentation and fixes a few minor bugs reported since the previous release. HttpCore now comes with a complete tutorial presenting an in-depth coverage of the API. HttpCore is a set of low level HTTP transport components that can be used to build custom client and server-side HTTP services with a minimal footprint. HttpCore supports two I/O models: blocking I/O model based on the classic Java I/O and non-blocking, event driven I/O model based on Java NIO. The blocking I/O model may be more appropriate for data intensive, low latency scenarios, whereas the non-blocking model may be more appropriate for high latency scenarios where raw data throughput is less important than the ability to handle thousands of simultaneous HTTP connections in a resource efficient manner. * [HTTPCORE-180] Fixed NPE in standard I/O event dispatchers when IOEventDispatch#disconnected fires before the session was fully initialized (IOEventDispatch#connected was not called). Contributed by Oleg Kalnichevski * [HTTPCORE-175] Chunk decoders no longer accept truncated chunks as valid input. Contributed by Oleg Kalnichevski Release 4.0 Beta 3 ------------------- The third BETA version of HttpComponents Core has been released. This is a maintenance release, which addresses a number of issues discovered since the previous release. The only significant new feature is an addition of an OSGi compliant bundle combining HttpCore and HttpCore NIO jars. * [HTTPCORE-173] Tolerate missing closing chunk if the chunk coded content is terminated by the end of stream (EOF) condition. Contributed by Oleg Kalnichevski * [HTTPCORE-174] Position is incremented twice in ContentLengthInputStream#skip(long) Contributed by Ildar Safarov * [HTTPCORE-125] OSGi bundle containing HttpCore & HttpCore NIO jars. Contributed by Oleg Kalnichevski * CancelledKeyException thrown in BaseIOReactor#validate() no longer causes a premature I/O reactor shutdown. Contributed by Oleg Kalnichevski * [HTTPCORE-172] Added #close() method to SharedInputBuffer and SharedOutputBuffer. The purpose of the new method is to close the buffer in case of normal / orderly termination of the underlying HTTP connection. Use #shutdown() method to force-close the buffer in case of abnormal / exceptional termination of the underlying connection. Contributed by Oleg Kalnichevski * [HTTPCORE-170] Fixed race condition in SharedOutputBuffer. Contributed by Jason Walton * [HTTPCORE-169] Fixed bug causing connecting I/O reactors to shut down due to ClosedChannelException if a pending session request is cancelled before the new channel has been registered with the selector. Contributed by Anders Wallgren * [HTTPCORE-167] Fixed handling the end of stream (EOF) condition in the #isStale() check of blocking HTTP connections. Contributed by Oleg Kalnichevski * [HTTPCORE-166] NIO reactors now maintain an audit log of fatal exceptions, which can be used to examine the cause and problems experienced during the shutdown process. Contributed by Oleg Kalnichevski * [HTTPCORE-165] Improved handling of CancelledKeyException in I/O reactors Contributed by Oleg Kalnichevski Release 4.0 Beta 2 ------------------- The second BETA version of HttpComponents Core has been released. This release adds a number of improvements to the NIO components, most notable being improved asynchronous client side and server-side protocol handlers. There has been a number of important bug fixes in HttpCore NIO module, whereas HttpCore base module has had very few changes. All upstream projects dependent on HttpCore NIO are strongly advised to upgrade. * [HTTPCORE-163] Fixed AbstractMultiworkerIOReactor#execute() to correctly propagate the original I/O exception in case of an abnormal termination. Contributed by Patrick Moore * Changed behavior of IdentityDecoder & LengthDelimitedDecoder to throw an IOException if data is attempted to be written beyond the length of a FileChannel. Previously would write nothing. Contributed by Sam Berlin * Fixed bug in LengthDelimitedDecoder & IdentityDecoder that caused transfers to a FileChannel to overwrite arbitrary parts of the file, if data was buffered in SessionInputBuffer. Contributed by Sam Berlin * Fixed concurrency bug in the ThrottlingHttpServerHandler protocol handler. Contributed by Oleg Kalnichevski * Fixed bug in SharedInputBuffer that caused input events to be incorrectly suspended. Contributed by Asankha C. Perera * [HTTPCORE-150] Entity implementation that serializes a Java object Contributed by Andrea Selva * [HTTPCORE-157] ChunkedOutputStream#flush() now behaves consistently with the specification of OutputStream#flush(). Contributed by Oleg Kalnichevski * [HTTPCORE-147] Fixed handling of requests with partially consumed content in ThrottlingHttpServiceHandler. Contributed by Oleg Kalnichevski * [HTTPCORE-92] ChunkEncoder splits input data larger than available space in the session output buffer into smaller chunks instead of expanding the buffer. Contributed by Andrea Selva and Oleg Kalnichevski * [HTTPCORE-149] I/O reactors now count period of inactivity since the time of the last read or write operation. Previously only read operations resulted in timeout counter reset. Contributed by Oleg Kalnichevski * [HTTPCORE-148] Improved asynchronous server and client HTTP protocol handler implementations. Contributed by Sam Berlin * [HTTPCORE-143] Ensure the underlying channel is closed if the session request is canceled or times out. Contributed by Oleg Kalnichevski * [HTTPCORE-140] Fixed timeout handling in ThrottlingHttpServiceHandler. Contributed by Lorenzo Moretti and Oleg Kalnichevski Release 4.0 Beta 1 ------------------- The first BETA version of HttpComponents Core has been released. This release can be considered a major milestone, as it marks the end of API instability in HttpCore. As of this release the API compatibility between minor releases in 4.x codeline will be maintained. This release includes several major improvements such as enhanced HTTP message parsing API and optimized parser implementations, Java 5.0 compatibility for HttpCore NIO extensions. Upstream projects are strongly encouraged to upgrade to the latest release. The focus of the development efforts will be gradually shifting towards providing better test coverage, documentation and performance optimizations. Change Log: ---------- * [HTTPCORE-141] Session request timeout in DefaultConnectingIOReactor invalidates the request. Contributed by Oleg Kalnichevski * [HTTPCORE-137] DefaultHttpRequestFactory extended to support all methods specified in RFC 2616 (except CONNECT). Contributed by Oleg Kalnichevski * Replaced HTTP parameter linking with a simple child/parent stack. Contributed by Oleg Kalnichevski * [HTTPCORE-134] all serialVersionUID attributes are private Contributed by Roland Weber * [HTTPCORE-133] Clone support for basic HTTP message elements and non-streaming entities. Contributed by Oleg Kalnichevski * [HTTPCORE-127] Improved API for lifecycle management of listening I/O reactors. One can now suspend and resume listener endpoints. Contributed by Asankha C. Perera * [HTTPCORE-112] DefaultConnectionReuseStrategy interprets token sequences Contributed by Roland Weber * [HTTPCORE-122] new interface TokenIterator and basic implementation Contributed by Roland Weber * HttpCore NIOSSL classes moved to HttpCore NIO. Contributed by Oleg Kalnichevski * HttpCore NIO ported to Java 1.5. Contributed by Oleg Kalnichevski * [HTTPCORE-130] Fixed over-synchronization bug leading to a thread deadlock condition in SSL IOEventDispatch implementations. Contributed by Oleg Kalnichevski * [HTTPCORE-37] HttpParams beans Contributed by Stojce Dimski * [HTTPCORE-128] Simplified injection of custom NIO connection implementations. Contributed by Oleg Kalnichevski * [HTTPCORE-126] Improved HTTP message parsing API and optimized parser implementations. Contributed by Oleg Kalnichevski * Do not include "Connection: close" to 500 responses per default. Contributed by Oleg Kalnichevski * [HTTPCORE-121] new interface HeaderElementIterator Contributed by Andrea Selva * [HTTPCORE-123] Fixed problem with SSLSession losing buffered data, if the connection has been closed by the peer. Contributed by Risto Reinpõld Release 4.0 Alpha 6 ------------------- The sixth ALPHA version of HttpComponents Core has been released. This release sports an improved message parsing and formatting API in the base module and lots of incremental improvements and bug fixes in the NIO and NIOSSL modules. Based on the improved API, it is now possible to send and receive SIP messages with HttpComponents Core. HttpCore is now feature complete and we are planning to freeze the public APIs as of next release (BETA1). * [HTTPCORE-120] new interface HeaderIterator, available from HttpMessage Contributed by Roland Weber * [HTTPCORE-118] Purge closed sessions prior to opening new ones. This should reduce chances of running out of memory when opening and closing lots of NIO connections in a tight loop. Contributed by Oleg Kalnichevski * [HTTPCORE-117] Fixed bug preventing protocol handlers from closing timed out NIO connection when pending output (output session buffer is not empty). Contributed by Oleg Kalnichevski * [HTTPCORE-86] Allow for optional handling of runtime exceptions thrown by protocol handlers to ensure the I/O dispatch thread remains running. Contributed by Oleg Kalnichevski * [HTTPCORE-116] moved parameter names to interfaces Contributed by Roland Weber * [HTTPCORE-109] Improved shutdown process of the I/O reactors in NIO modules. I/O reactors now attempt to terminate connections gracefully before shutting down the underlying socket channels. Contributed by Oleg Kalnichevski * [HTTPCORE-107] allow sending and receiving of SIP messages Contributed by Roland Weber * [HTTPCORE-114]: Fixed incorrect handling of the end-of-stream condition in SSLIOSession. Oleg Kalnichevski * [HTTPCORE-110] refactored message parsing and formatting logic Contributed by Roland Weber * [HTTPCORE-113] Removed unnecessary target hostname resolution from non-blocking client protocol handlers. Oleg Kalnichevski * [HTTPCORE-108] Close all channels registered with the I/O reactor during shutdown. Fixed the problem with DefaultListeningIOReactor not releasing socket ports until JVM is restarted. Oleg Kalnichevski * [HTTPCORE-106] Pluggable HTTP message parsers and message writers Oleg Kalnichevski * [HTTPCORE-105] Consistent class names in base and NIO modules Oleg Kalnichevski * [HTTPCORE-100] revised HttpContext hierarchy Contributed by Roland Weber * [HTTPCORE-103] NIO connections now attempt to consume all available session data while parsing HTTP messages. This can potentially improve performance of non-blocking SSL connections. Contributed by Steffen Pingel * [HTTPCORE-102] Exceeding of maximum line length limit detected late Contributed by Steffen Pingel * [HTTPCORE-21] Transport and connection metrics Contributed by Andrea Selva and Oleg Kalnichevski * [HTTPCORE-91] new interceptor RequestDate, renamed constants in protocol.HTTP Contributed by Roland Weber * [HTTPCORE-90] Version detection based on build-time properties Contributed by Oleg Kalnichevski and Roland Weber * [HTTPCORE-88] Added convenience methods to HttpRequestInterceptorList, HttpResponseInterceptorList interfaces Contributed by Andrea Selva * [HTTPCORE-89]: Fixed bug in DefaultConnectingIOReactor causing incorrect handling of local (immediate) connections on some platforms (affects Sun Solaris 2.9 / Sparc and likely other Solaris 2.x platforms) Contributed by Sam Berlin Release 4.0 Alpha 5 ------------------- The fifth ALPHA version of HttpComponents Core has been released. This release delivers a number of incremental improvements across the board in all modules and adds several performance oriented features such as ability to transfer data directly between a file and a socket channels. HttpCore is almost fully feature complete now and we are likely to freeze the public APIs as of next release (BETA1). * [HTTPCORE-87] RuntimeExcpetions thrown in I/O worker threads are now correctly propagated to the I/O reactor. Contributed by Oleg Kalnichevski * [HTTPCORE-84]: Removed DateUtils/DateParseException from core. Contributed by Roland Weber * [HTTPCORE-63]: Made I/O select interval configurable for all default I/O reactor implementations. Contributed by Oleg Kalnichevski and Anders Wallgren * [HTTPCORE-82]: Revised linking of HttpParams to reduce potential for misuse. Method #setDefaults() removed from the HttpParams interface. Contributed by Roland Weber * [HTTPCORE-81]: Maximum line length and maximum header counts parameters are now correctly enforced in both base and NIO modules. Fixed maximum line length check when parsing folded header lines. Contributed by Steffen Pingel * Added HTTP client handler implementation that allocates fixed size content buffers upon initialization and is capable of throttling the rate of I/O events in order to make sure those content buffers do not get overflown. Contributed by Oleg Kalnichevski * [HTTPCORE-76]: Added IOSession#shutdown() method intended to force-close I/O sessions (primarily needed to terminate hung SSL connections). Contributed by Sandeep Tamhankar * [HTTPCORE-78]: Added ByteBufferAllocator interface that can be used to apply different ByteArray allocation strategies to session and content buffers. Use heap bound implementation for short-lived or variable in length (requiring frequent content re-allocation) buffers. Contributed by Steffen Pingel * [HTTPCORE-77]: The result of CharsetDecoder#decode() and CharsetEncoder#encode() was not checked for errors resulting in an infinite loop in SessionInputBuffer#readLine() and SessionOutputBuffer#writeLine() when malformed characters were processed. Contributed by Steffen Pingel * [HTTPCORE-71]: HttpParams can be copied. Contributed by Roland Weber * [HTTPCORE-75]: DefaultNHttpServerConnection and DefaultNHttpClientConnection now correctly terminate the underlying I/O session when closed. BufferingHttpServiceHandler now correctly applies connection keep-alive strategy when sending a response with no content body. Contributed by Steffen Pingel * [HTTPCORE-73]: Fixed bug preventing NHttpServiceHandler#responseReady and NHttpClientHandler#requestReady events from being fired if the HTTP message has no content body. Contributed by Steffen Pingel * [HTTPCORE-67]: Improved event listener interface Contributed by Oleg Kalnichevski * [HTTPCORE-43]: Support for FileChannel#transferFrom() and FileChannel#transferTo() methods. Direct coping from and to FileChannel is expected to improve performance of file bound operations Contributed by Andrea Selva * [HTTPCORE-66]: Fixed handling of HTTP HEAD methods Contributed by Steffen Pingel and Oleg Kalnichevski * [HTTPCORE-58]: NIO HTTP connections changed to throw checked ConnectionClosedException instead of unchecked IllegalStateException when an attempt is made to perform an I/O operation on a closed conection Contributed by Oleg Kalnichevski * [HTTPCORE-56]: DefaultConnectingIOReactor no longer terminates due to a CancelledKeyException, if a session request gets canceled before selection key is fully initialized. Contributed by Oleg Kalnichevski Release 4.0 Alpha 4 ------------------- The forth ALPHA version of HttpComponents Core has been released. The ALPHA4 release fixes a number of bugs and adds a number of improvements to HttpCore base and HttpCore NIO extensions. HttpCore NIO can be used to build HTTP services intended to handle thousands of simultaneous connections with a small number of I/O threads. This release also introduces NIOSSL extensions that can be used to extend HttpCore non-blocking transport components with ability to transparently encrypt data in transit using SSL/TLS protocol. * [HTTPCORE-49]: DefaultConnectingIOReactor can now correctly handle unresolved socket addresses. It no longer terminates with the UnresolvedAddressException runtime exception. Contributed by Oleg Kalnichevski * [HTTPCORE-42]: Added server-side API for the expectation verification. Improved support for the 'expect: continue' handshake in HttpCore and HttpCore NIO. Contributed by Oleg Kalnichevski * [HTTPCORE-26]: Added SSL support for HttpCore NIO. Contributed by Oleg Kalnichevski * [HTTPCORE-40]: API classes no longer reference impl classes in module-main. Contributed by Roland Weber * [HTTPCORE-39]: Refactored HttpStatus, spun off [English]ReasonPhraseFactory. Contributed by Roland Weber * [HTTPCORE-32]: HttpRequestInterceptorList, HttpResponseInterceptorList Contributed by Roland Weber * [HTTPCORE-38]: Packages nio.impl.* are now impl.nio.*, same for examples. Contributed by Roland Weber * [HTTPCORE-27]: I/O reactors can now accept a thread factory as an optional parameter. Contributed by Oleg Kalnichevski * [HTTPCORE-36]: Fixed #setHandlers() method and matching of request URIs with a query part in HttpRequestHandlerRegistry Contributed by Oleg Kalnichevski * [HTTPCORE-28]: DefaultConnectingIOReactor now maintains a queue of connect requests and registers new sessions with the selector on the I/O thread. Contributed by Oleg Kalnichevski * [HTTPCORE-29] DefaultConnectingIOReactor changed to ensure IOExceptions are correctly propagated to the caller, if an exception is thrown while initializing a newly connected socket. Contributed by Oleg Kalnichevski * [HTTPCORE-24] Fixed bug in non-blocking connection implementations, which prevented the session buffer from being correctly flushed when the content coding process has been completed. Contributed by Oleg Kalnichevski * [HTTPCORE-23] Fixed threading bug in DefaultConnectingIOReactor. Contributed by Asankha C. Perera Release 4.0 Alpha 3 ------------------- The third ALPHA version of HttpCore has been released. The ALPHA3 release includes a number of API optimizations and improvements and introduces a set of NIO extensions to the HttpCore API. NIO extensions can be used to build HTTP services intended to handle thousands of simultaneous connections with a small number of I/O threads. * [HTTPCORE-15] Provided a interafce to access IP address of the local and remote end points. Contributed by Oleg Kalnichevski * [ HTTPCORE-14] Scheme, SocketFactory and SecureSocketFactory moved to HttpClient. Decoupled HttpHost and Scheme. Contributed by Oleg Kalnichevski * [HTTPCORE-13] Refactored HttpProcessor interface and related impl classes Contributed by Roland Weber * [HTTPCORE-11] Client connection interface no longer defines a specific method to open a connection. HTTP connections can now represent any abstract I/O transport such as those based on NIO API. Contributed by Oleg Kalnichevski * [HTTPCORE-10] Non-blocking (async) client side I/O transport based on NIO. Contributed by Oleg Kalnichevski * [HTTPCORE-9] Non-blocking (async) server-side I/O transport based on NIO. Contributed by Oleg Kalnichevski * [HTTPCORE-7] ConnectionReuseStrategy interface changed to allow access to the HTTP execution context. Contributed by Roland Weber * [HTTPCORE-6] Header implementation allowing for performance short-cuts when serializing and deserializing HTTP headers. Contributed by Oleg Kalnichevski * [HTTPCORE-5] Header, HeaderElement, NameValuePair, RequestLine, StatusLine, HttpVersion changed to interfaces. API no longer contains any parsing and formatting code and does not imply any specific physical representation of HTTP messages and their elements. Contributed by Oleg Kalnichevski Release 4.0 Alpha 2 ------------------- This is a maintenance release that mostly fixes minor problems found since the previous release. The upstream projects are strongly encouraged use this release as a dependency while HttpCore undergoes another round of reviews and optimization in the SVN trunk Change Log: --------- * [HTTPCORE-4] optional header and line length limits to contain OOME risks Contributed by Oleg Kalnichevski Release 4.0 Alpha 1 ------------------- This release represents a complete redesign of the Jakarta Commons HttpClient 3.x API and a significant rewrite of the core HTTP components derived from HttpClient 3.0 code base. These components will form the foundation of the future releases of Jakarta HttpClient and can also be used separately to build custom client- and server-side HTTP services. test-CA/0040775 0000000 0000000 00000000000 14245617503 011061 5ustar000000000 0000000 test-CA/README.txt0100664 0000000 0000000 00000000456 14245617503 012561 0ustar000000000 0000000 This directory contains CA key and certificate for unit and integration tests --- Use this command to check the private key Passphrase: nopassword --- openssl rsa -in ca-key.pem -check -text -noout --- Use this command to print CA certificate details --- openssl x509 -in ca-cert.pem -text -noout ---httpcore5/0040775 0000000 0000000 00000000000 14443065100 011524 5ustar000000000 0000000 httpcore5/src/0040775 0000000 0000000 00000000000 14245617503 012325 5ustar000000000 0000000 httpcore5/src/main/0040775 0000000 0000000 00000000000 14245617503 013251 5ustar000000000 0000000 httpcore5/src/main/resources/0040775 0000000 0000000 00000000000 14245617503 015263 5ustar000000000 0000000 httpcore5/src/main/resources/org/0040775 0000000 0000000 00000000000 14245617503 016052 5ustar000000000 0000000 httpcore5/src/main/resources/org/apache/0040775 0000000 0000000 00000000000 14245617503 017273 5ustar000000000 0000000 httpcore5/src/main/resources/org/apache/hc/0040775 0000000 0000000 00000000000 14245617503 017665 5ustar000000000 0000000 httpcore5/src/main/resources/org/apache/hc/core5/0040775 0000000 0000000 00000000000 14245617503 020702 5ustar000000000 0000000 httpcore5/src/main/resources/org/apache/hc/core5/version.properties0100664 0000000 0000000 00000001540 14245617503 024502 0ustar000000000 0000000 # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # info.module = ${project.artifactId} info.release = ${project.version} httpcore5/src/main/java/0040775 0000000 0000000 00000000000 14245617503 014172 5ustar000000000 0000000 httpcore5/src/main/java/org/0040775 0000000 0000000 00000000000 14245617503 014761 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/0040775 0000000 0000000 00000000000 14245617503 016202 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/0040775 0000000 0000000 00000000000 14245617503 016574 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/0040775 0000000 0000000 00000000000 14245617503 017611 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/net/0040775 0000000 0000000 00000000000 14435411677 020404 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/net/package-info.java0100664 0000000 0000000 00000002370 14245617503 023565 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core network component APIs and utilities. */ package org.apache.hc.core5.net; httpcore5/src/main/java/org/apache/hc/core5/net/WWWFormCodec.java0100664 0000000 0000000 00000005531 14245617503 023511 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import java.nio.charset.Charset; import java.util.List; import org.apache.hc.core5.http.NameValuePair; /** * {@code application/x-www-form-urlencoded} codec. * * @since 5.1 */ public class WWWFormCodec { private static final char QP_SEP_A = '&'; /** * Returns a list of {@link NameValuePair} parameters parsed * from the {@code application/x-www-form-urlencoded} content. * * @param s input text. * @param charset parameter charset. * @return list of form parameters. */ public static List parse(final CharSequence s, final Charset charset) { return URIBuilder.parseQuery(s, charset, true); } /** * Formats the list of {@link NameValuePair} parameters into a {@code application/x-www-form-urlencoded} * content. * * @param buf the content buffer * @param params The from parameters. * @param charset The encoding to use. */ public static void format( final StringBuilder buf, final Iterable params, final Charset charset) { URIBuilder.formatQuery(buf, params, charset, true); } /** * Formats the list of {@link NameValuePair} parameters into a {@code application/x-www-form-urlencoded} * content string. * * @param params The from parameters. * @param charset The encoding to use. * @return content string */ public static String format(final Iterable params, final Charset charset) { final StringBuilder buf = new StringBuilder(); URIBuilder.formatQuery(buf, params, charset, true); return buf.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/net/NamedEndpoint.java0100664 0000000 0000000 00000003122 14245617503 023762 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; /** * Endpoint identified by name (usually a fully qualified domain name) and port. * * @since 5.0 */ public interface NamedEndpoint { /** * Returns name (IP or DNS name). * * @return the host name (IP or DNS name) */ String getHostName(); /** * Returns the port. * * @return the host port, or {@code -1} if not set */ int getPort(); } httpcore5/src/main/java/org/apache/hc/core5/net/InetAddressUtils.java0100664 0000000 0000000 00000015530 14435411677 024476 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnknownHostException; import java.util.regex.Pattern; import org.apache.hc.core5.util.Args; /** * A collection of utilities relating to InetAddresses. * * @since 4.0 */ public class InetAddressUtils { /** * Represents the ipv4 * * @since 5.1 */ public static final byte IPV4 = 1; /** * Represents the ipv6. * * @since 5.1 */ public static final byte IPV6 = 4; private InetAddressUtils() { } private static final String IPV4_BASIC_PATTERN_STRING = "(([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){1}" + // initial first field, 1-255 "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){2}" + // following 2 fields, 0-255 followed by . "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"; // final field, 0-255 private static final Pattern IPV4_PATTERN = Pattern.compile("^" + IPV4_BASIC_PATTERN_STRING + "$"); private static final Pattern IPV4_MAPPED_IPV6_PATTERN = // TODO does not allow for redundant leading zeros Pattern.compile("^::[fF]{4}:" + IPV4_BASIC_PATTERN_STRING + "$"); private static final Pattern IPV6_STD_PATTERN = Pattern.compile( "^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$"); private static final Pattern IPV6_HEX_COMPRESSED_PATTERN = Pattern.compile( "^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)" + // 0-6 hex fields "::" + "(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)$"); // 0-6 hex fields /* * The above pattern is not totally rigorous as it allows for more than 7 hex fields in total */ private static final char COLON_CHAR = ':'; // Must not have more than 7 colons (i.e. 8 fields) private static final int MAX_COLON_COUNT = 7; /** * Checks whether the parameter is a valid IPv4 address * * @param input the address string to check for validity * @return true if the input parameter is a valid IPv4 address */ public static boolean isIPv4Address(final String input) { return IPV4_PATTERN.matcher(input).matches(); } public static boolean isIPv4MappedIPv64Address(final String input) { return IPV4_MAPPED_IPV6_PATTERN.matcher(input).matches(); } static boolean hasValidIPv6ColonCount(final String input) { int colonCount = 0; for (int i = 0; i < input.length(); i++) { if (input.charAt(i) == COLON_CHAR) { colonCount++; } } // IPv6 address must have at least 2 colons and not more than 7 (i.e. 8 fields) return colonCount >= 2 && colonCount <= MAX_COLON_COUNT; } /** * Checks whether the parameter is a valid standard (non-compressed) IPv6 address * * @param input the address string to check for validity * @return true if the input parameter is a valid standard (non-compressed) IPv6 address */ public static boolean isIPv6StdAddress(final String input) { return hasValidIPv6ColonCount(input) && IPV6_STD_PATTERN.matcher(input).matches(); } /** * Checks whether the parameter is a valid compressed IPv6 address * * @param input the address string to check for validity * @return true if the input parameter is a valid compressed IPv6 address */ public static boolean isIPv6HexCompressedAddress(final String input) { return hasValidIPv6ColonCount(input) && IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches(); } /** * Checks whether the parameter is a valid IPv6 address (including compressed). * * @param input the address string to check for validity * @return true if the input parameter is a valid standard or compressed IPv6 address */ public static boolean isIPv6Address(final String input) { return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input); } /** * Checks whether the parameter is a valid URL formatted bracketed IPv6 address (including compressed). * This matches only bracketed values e.g. {@code [::1]}. * * @param input the address string to check for validity * @return true if the input parameter is a valid URL-formatted bracketed IPv6 address */ public static boolean isIPv6URLBracketedAddress(final String input) { return input.startsWith("[") && input.endsWith("]") && isIPv6Address(input.substring(1, input.length() - 1)); } /** * Formats {@link SocketAddress} as text. * * @since 5.0 */ public static void formatAddress( final StringBuilder buffer, final SocketAddress socketAddress) { Args.notNull(buffer, "buffer"); if (socketAddress instanceof InetSocketAddress) { final InetSocketAddress socketaddr = (InetSocketAddress) socketAddress; final InetAddress inetaddr = socketaddr.getAddress(); if (inetaddr != null) { buffer.append(inetaddr.getHostAddress()).append(':').append(socketaddr.getPort()); } else { buffer.append(socketAddress); } } else { buffer.append(socketAddress); } } /** * Returns canonical name (fully qualified domain name) of the localhost. * * @since 5.0 */ public static String getCanonicalLocalHostName() { try { final InetAddress localHost = InetAddress.getLocalHost(); return localHost.getCanonicalHostName(); } catch (final UnknownHostException ex) { return "localhost"; } } } httpcore5/src/main/java/org/apache/hc/core5/net/Ports.java0100664 0000000 0000000 00000005427 14245617503 022356 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import org.apache.hc.core5.util.Args; /** * Port helper methods. * * @since 5.0 */ public class Ports { /** * The scheme default port. */ public final static int SCHEME_DEFAULT = -1; /** * The minimum port value per https://tools.ietf.org/html/rfc6335. */ public final static int MIN_VALUE = 0; /** * The maximum port value per https://tools.ietf.org/html/rfc6335. */ public final static int MAX_VALUE = 65535; /** * Checks a port number where {@code -1} indicates the scheme default port. * * @param port * The port to check where {@code -1} indicates the scheme default port. * @return the port * * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. {@code -1} indicates the scheme default port. */ public static int checkWithDefault(final int port) { return Args.checkRange(port, SCHEME_DEFAULT, MAX_VALUE, "Port number(Use -1 to specify the scheme default port)"); } /** * Checks a port number. * * @param port * The port to check. * @return the port * * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. */ public static int check(final int port) { return Args.checkRange(port, MIN_VALUE, MAX_VALUE, "Port number"); } } httpcore5/src/main/java/org/apache/hc/core5/net/PercentCodec.java0100664 0000000 0000000 00000013053 14245617503 023577 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.BitSet; /** * Percent-encoding mechanism defined in RFC 3986 * * @since 5.1 */ public class PercentCodec { static final BitSet GEN_DELIMS = new BitSet(256); static final BitSet SUB_DELIMS = new BitSet(256); static final BitSet UNRESERVED = new BitSet(256); static final BitSet URIC = new BitSet(256); static { GEN_DELIMS.set(':'); GEN_DELIMS.set('/'); GEN_DELIMS.set('?'); GEN_DELIMS.set('#'); GEN_DELIMS.set('['); GEN_DELIMS.set(']'); GEN_DELIMS.set('@'); SUB_DELIMS.set('!'); SUB_DELIMS.set('$'); SUB_DELIMS.set('&'); SUB_DELIMS.set('\''); SUB_DELIMS.set('('); SUB_DELIMS.set(')'); SUB_DELIMS.set('*'); SUB_DELIMS.set('+'); SUB_DELIMS.set(','); SUB_DELIMS.set(';'); SUB_DELIMS.set('='); for (int i = 'a'; i <= 'z'; i++) { UNRESERVED.set(i); } for (int i = 'A'; i <= 'Z'; i++) { UNRESERVED.set(i); } // numeric characters for (int i = '0'; i <= '9'; i++) { UNRESERVED.set(i); } UNRESERVED.set('-'); UNRESERVED.set('.'); UNRESERVED.set('_'); UNRESERVED.set('~'); URIC.or(SUB_DELIMS); URIC.or(UNRESERVED); } private static final int RADIX = 16; static void encode(final StringBuilder buf, final CharSequence content, final Charset charset, final BitSet safechars, final boolean blankAsPlus) { if (content == null) { return; } final CharBuffer cb = CharBuffer.wrap(content); final ByteBuffer bb = (charset != null ? charset : StandardCharsets.UTF_8).encode(cb); while (bb.hasRemaining()) { final int b = bb.get() & 0xff; if (safechars.get(b)) { buf.append((char) b); } else if (blankAsPlus && b == ' ') { buf.append("+"); } else { buf.append("%"); final char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, RADIX)); final char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX)); buf.append(hex1); buf.append(hex2); } } } static void encode(final StringBuilder buf, final CharSequence content, final Charset charset, final boolean blankAsPlus) { encode(buf, content, charset, UNRESERVED, blankAsPlus); } public static void encode(final StringBuilder buf, final CharSequence content, final Charset charset) { encode(buf, content, charset, UNRESERVED, false); } public static String encode(final CharSequence content, final Charset charset) { if (content == null) { return null; } final StringBuilder buf = new StringBuilder(); encode(buf, content, charset, UNRESERVED, false); return buf.toString(); } static String decode(final CharSequence content, final Charset charset, final boolean plusAsBlank) { if (content == null) { return null; } final ByteBuffer bb = ByteBuffer.allocate(content.length()); final CharBuffer cb = CharBuffer.wrap(content); while (cb.hasRemaining()) { final char c = cb.get(); if (c == '%' && cb.remaining() >= 2) { final char uc = cb.get(); final char lc = cb.get(); final int u = Character.digit(uc, RADIX); final int l = Character.digit(lc, RADIX); if (u != -1 && l != -1) { bb.put((byte) ((u << 4) + l)); } else { bb.put((byte) '%'); bb.put((byte) uc); bb.put((byte) lc); } } else if (plusAsBlank && c == '+') { bb.put((byte) ' '); } else { bb.put((byte) c); } } bb.flip(); return (charset != null ? charset : StandardCharsets.UTF_8).decode(bb).toString(); } public static String decode(final CharSequence content, final Charset charset) { return decode(content, charset, false); } } httpcore5/src/main/java/org/apache/hc/core5/net/URLEncodedUtils.java0100664 0000000 0000000 00000021130 14403631147 024175 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.List; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Tokenizer; /** * A collection of utilities for encoding URLs. * * @since 4.0 * * @deprecated Use {@link URIBuilder} to parse and format {@link URI}s and * {@link WWWFormCodec} to parse and format {@code application/x-www-form-urlencoded} forms. */ @Deprecated public class URLEncodedUtils { private static final char QP_SEP_A = '&'; private static final char QP_SEP_S = ';'; /** * Returns a list of {@link NameValuePair}s URI query parameters. * By convention, {@code '&'} and {@code ';'} are accepted as parameter separators. * * @param uri input URI. * @param charset parameter charset. * @return list of query parameters. * * @since 4.5 */ public static List parse(final URI uri, final Charset charset) { Args.notNull(uri, "URI"); final String query = uri.getRawQuery(); if (query != null && !query.isEmpty()) { return parse(query, charset); } return new ArrayList<>(0); } /** * Returns a list of {@link NameValuePair}s URI query parameters. * By convention, {@code '&'} and {@code ';'} are accepted as parameter separators. * * @param s URI query component. * @param charset charset to use when decoding the parameters. * @return list of query parameters. * * @since 4.2 */ public static List parse(final CharSequence s, final Charset charset) { if (s == null) { return new ArrayList<>(0); } return parse(s, charset, QP_SEP_A, QP_SEP_S); } /** * Returns a list of {@link NameValuePair}s parameters. * * @param s input text. * @param charset parameter charset. * @param separators parameter separators. * @return list of query parameters. * * @since 4.4 */ public static List parse( final CharSequence s, final Charset charset, final char... separators) { Args.notNull(s, "Char sequence"); final Tokenizer tokenParser = Tokenizer.INSTANCE; final BitSet delimSet = new BitSet(); for (final char separator: separators) { delimSet.set(separator); } final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); final List list = new ArrayList<>(); while (!cursor.atEnd()) { delimSet.set('='); final String name = tokenParser.parseToken(s, cursor, delimSet); String value = null; if (!cursor.atEnd()) { final int delim = s.charAt(cursor.getPos()); cursor.updatePos(cursor.getPos() + 1); if (delim == '=') { delimSet.clear('='); value = tokenParser.parseToken(s, cursor, delimSet); if (!cursor.atEnd()) { cursor.updatePos(cursor.getPos() + 1); } } } if (!name.isEmpty()) { list.add(new BasicNameValuePair( PercentCodec.decode(name, charset, true), PercentCodec.decode(value, charset, true))); } } return list; } /** * Returns a list of URI path segments. * * @param s URI path component. * @param charset parameter charset. * @return list of segments. * * @since 4.5 */ public static List parsePathSegments(final CharSequence s, final Charset charset) { return URIBuilder.parsePath(s, charset); } /** * Returns a list of URI path segments. * * @param s URI path component. * @return list of segments. * * @since 4.5 */ public static List parsePathSegments(final CharSequence s) { return parsePathSegments(s, StandardCharsets.UTF_8); } /** * Returns a string consisting of joint encoded path segments. * * @param segments the segments. * @param charset parameter charset. * @return URI path component * * @since 4.5 */ public static String formatSegments(final Iterable segments, final Charset charset) { Args.notNull(segments, "Segments"); final StringBuilder buf = new StringBuilder(); URIBuilder.formatPath(buf, segments, false, charset); return buf.toString(); } /** * Returns a string consisting of joint encoded path segments. * * @param segments the segments. * @return URI path component * * @since 4.5 */ public static String formatSegments(final String... segments) { return formatSegments(Arrays.asList(segments), StandardCharsets.UTF_8); } /** * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded} * list of parameters in an HTTP PUT or HTTP POST. * * @param parameters The parameters to include. * @param parameterSeparator The parameter separator, by convention, {@code '&'} or {@code ';'}. * @param charset The encoding to use. * @return An {@code application/x-www-form-urlencoded} string * * @since 4.3 */ public static String format( final Iterable parameters, final char parameterSeparator, final Charset charset) { Args.notNull(parameters, "Parameters"); final StringBuilder buf = new StringBuilder(); int i = 0; for (final NameValuePair parameter : parameters) { if (i > 0) { buf.append(parameterSeparator); } PercentCodec.encode(buf, parameter.getName(), charset, URL_ENCODER, true); if (parameter.getValue() != null) { buf.append('='); PercentCodec.encode(buf, parameter.getValue(), charset, URL_ENCODER, true); } i++; } return buf.toString(); } /** * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded} * list of parameters in an HTTP PUT or HTTP POST. * * @param parameters The parameters to include. * @param charset The encoding to use. * @return An {@code application/x-www-form-urlencoded} string * * @since 4.2 */ public static String format( final Iterable parameters, final Charset charset) { return format(parameters, QP_SEP_A, charset); } private static final BitSet URL_ENCODER = new BitSet(256); static { // unreserved chars // alpha characters for (int i = 'a'; i <= 'z'; i++) { URL_ENCODER.set(i); } for (int i = 'A'; i <= 'Z'; i++) { URL_ENCODER.set(i); } // numeric characters for (int i = '0'; i <= '9'; i++) { URL_ENCODER.set(i); } URL_ENCODER.set('_'); // these are the characters of the "mark" list URL_ENCODER.set('-'); URL_ENCODER.set('.'); URL_ENCODER.set('*'); } } httpcore5/src/main/java/org/apache/hc/core5/net/URISupport.java0100664 0000000 0000000 00000004264 14245617503 023301 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import java.net.URISyntaxException; import java.util.BitSet; import org.apache.hc.core5.util.Tokenizer; final class URISupport { static final BitSet HOST_SEPARATORS = new BitSet(256); static final BitSet IPV6_HOST_TERMINATORS = new BitSet(256); static final BitSet PORT_SEPARATORS = new BitSet(256); static final BitSet TERMINATORS = new BitSet(256); static { TERMINATORS.set('/'); TERMINATORS.set('#'); TERMINATORS.set('?'); HOST_SEPARATORS.or(TERMINATORS); HOST_SEPARATORS.set('@'); IPV6_HOST_TERMINATORS.set(']'); PORT_SEPARATORS.or(TERMINATORS); PORT_SEPARATORS.set(':'); } static URISyntaxException createException( final CharSequence input, final Tokenizer.Cursor cursor, final String reason) { return new URISyntaxException( input.subSequence(cursor.getLowerBound(), cursor.getUpperBound()).toString(), reason, cursor.getPos()); } } httpcore5/src/main/java/org/apache/hc/core5/net/Host.java0100664 0000000 0000000 00000014043 14435411677 022163 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import java.io.Serializable; import java.net.URISyntaxException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.LangUtils; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Tokenizer; /** * Component that holds all details needed to describe a network connection * to a host. This includes remote host name and port. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class Host implements NamedEndpoint, Serializable { private static final long serialVersionUID = 1L; private final String name; private final String lcName; private final int port; public Host(final String name, final int port) { super(); this.name = Args.notNull(name, "Host name"); this.port = Ports.checkWithDefault(port); this.lcName = TextUtils.toLowerCase(this.name); } static Host parse(final CharSequence s, final Tokenizer.Cursor cursor) throws URISyntaxException { final Tokenizer tokenizer = Tokenizer.INSTANCE; final String hostName; final boolean ipv6Brackets = !cursor.atEnd() && s.charAt(cursor.getPos()) == '['; if (ipv6Brackets) { cursor.updatePos(cursor.getPos() + 1); hostName = tokenizer.parseContent(s, cursor, URISupport.IPV6_HOST_TERMINATORS); if (cursor.atEnd() || !(s.charAt(cursor.getPos()) == ']')) { throw URISupport.createException(s, cursor, "Expected an IPv6 closing bracket ']'"); } cursor.updatePos(cursor.getPos() + 1); if (!InetAddressUtils.isIPv6Address(hostName)) { throw URISupport.createException(s, cursor, "Expected an IPv6 address"); } } else { hostName = tokenizer.parseContent(s, cursor, URISupport.PORT_SEPARATORS); } String portText = null; if (!cursor.atEnd() && s.charAt(cursor.getPos()) == ':') { cursor.updatePos(cursor.getPos() + 1); portText = tokenizer.parseContent(s, cursor, URISupport.TERMINATORS); } final int port; if (!TextUtils.isBlank(portText)) { if (!ipv6Brackets && portText.contains(":")) { throw URISupport.createException(s, cursor, "Expected IPv6 address to be enclosed in brackets"); } try { port = Integer.parseInt(portText); } catch (final NumberFormatException ex) { throw URISupport.createException(s, cursor, "Port is invalid"); } } else { port = -1; } return new Host(hostName, port); } static Host parse(final CharSequence s) throws URISyntaxException { final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); return parse(s, cursor); } static void format(final StringBuilder buf, final NamedEndpoint endpoint) { final String hostName = endpoint.getHostName(); if (InetAddressUtils.isIPv6Address(hostName)) { buf.append('[').append(hostName).append(']'); } else { buf.append(hostName); } if (endpoint.getPort() != -1) { buf.append(":"); buf.append(endpoint.getPort()); } } static void format(final StringBuilder buf, final Host host) { format(buf, (NamedEndpoint) host); } static String format(final Host host) { final StringBuilder buf = new StringBuilder(); format(buf, host); return buf.toString(); } public static Host create(final String s) throws URISyntaxException { Args.notEmpty(s, "HTTP Host"); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); final Host host = parse(s, cursor); if (TextUtils.isBlank(host.getHostName())) { throw URISupport.createException(s, cursor, "Hostname is invalid"); } if (!cursor.atEnd()) { throw URISupport.createException(s, cursor, "Unexpected content"); } return host; } @Override public String getHostName() { return name; } @Override public int getPort() { return port; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o instanceof Host) { final Host that = (Host) o; return this.lcName.equals(that.lcName) && this.port == that.port; } return false; } @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, this.lcName); hash = LangUtils.hashCode(hash, this.port); return hash; } @Override public String toString() { return format(this); } } httpcore5/src/main/java/org/apache/hc/core5/net/URIBuilder.java0100664 0000000 0000000 00000104437 14435411677 023223 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.http.message.ParserCursor; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Tokenizer; /** * Builder for {@link URI} instances. * * @since 5.0 */ public class URIBuilder { /** * Creates a new builder for the host {@link InetAddress#getLocalHost()}. * * @return a new builder. * @throws UnknownHostException if the local host name could not be resolved into an address. */ public static URIBuilder localhost() throws UnknownHostException { return new URIBuilder().setHost(InetAddress.getLocalHost()); } /** * Creates a new builder for the host {@link InetAddress#getLoopbackAddress()}. */ public static URIBuilder loopbackAddress() { return new URIBuilder().setHost(InetAddress.getLoopbackAddress()); } private String scheme; private String encodedSchemeSpecificPart; private String encodedAuthority; private String userInfo; private String encodedUserInfo; private String host; private int port; private String encodedPath; private boolean pathRootless; private List pathSegments; private String encodedQuery; private List queryParams; private String query; private Charset charset; private String fragment; private String encodedFragment; /** * Constructs an empty instance. */ public URIBuilder() { super(); this.port = -1; } /** * Constructs an instance from the string which must be a valid URI. * * @param uriString a valid URI in string form. * @throws URISyntaxException if the input is not a valid URI. */ public URIBuilder(final String uriString) throws URISyntaxException { this(new URI(uriString), StandardCharsets.UTF_8); } /** * Constructs an instance from the provided URI. * @param uri a URI. */ public URIBuilder(final URI uri) { this(uri, StandardCharsets.UTF_8); } /** * Constructs an instance from the string which must be a valid URI. * * @param uriString a valid URI in string form. * @throws URISyntaxException if the input is not a valid URI */ public URIBuilder(final String uriString, final Charset charset) throws URISyntaxException { this(new URI(uriString), charset); } /** * Constructs an instance from the provided URI. * * @param uri a URI. */ public URIBuilder(final URI uri, final Charset charset) { super(); digestURI(uri, charset); } /** * Sets the authority. * * @param authority the authority. * @return this. * @since 5.2 */ public URIBuilder setAuthority(final NamedEndpoint authority) { setUserInfo(null); setHost(authority.getHostName()); setPort(authority.getPort()); return this; } /** * Sets the authority. * * @param authority the authority. * @return this. * @since 5.2 */ public URIBuilder setAuthority(final URIAuthority authority) { setUserInfo(authority.getUserInfo()); setHost(authority.getHostName()); setPort(authority.getPort()); return this; } /** * Sets the Charset. * * @param charset the Charset. * @return this. */ public URIBuilder setCharset(final Charset charset) { this.charset = charset; return this; } /** * Gets the authority. * * @return the authority. * @since 5.2 */ public URIAuthority getAuthority() { return new URIAuthority(getUserInfo(), getHost(), getPort()); } /** * Gets the Charset. * * @return the Charset. */ public Charset getCharset() { return charset; } private static final char QUERY_PARAM_SEPARATOR = '&'; private static final char PARAM_VALUE_SEPARATOR = '='; private static final char PATH_SEPARATOR = '/'; private static final BitSet QUERY_PARAM_SEPARATORS = new BitSet(256); private static final BitSet QUERY_VALUE_SEPARATORS = new BitSet(256); private static final BitSet PATH_SEPARATORS = new BitSet(256); static { QUERY_PARAM_SEPARATORS.set(QUERY_PARAM_SEPARATOR); QUERY_PARAM_SEPARATORS.set(PARAM_VALUE_SEPARATOR); QUERY_VALUE_SEPARATORS.set(QUERY_PARAM_SEPARATOR); PATH_SEPARATORS.set(PATH_SEPARATOR); } static List parseQuery(final CharSequence s, final Charset charset, final boolean plusAsBlank) { if (s == null) { return null; } final Tokenizer tokenParser = Tokenizer.INSTANCE; final ParserCursor cursor = new ParserCursor(0, s.length()); final List list = new ArrayList<>(); while (!cursor.atEnd()) { final String name = tokenParser.parseToken(s, cursor, QUERY_PARAM_SEPARATORS); String value = null; if (!cursor.atEnd()) { final int delim = s.charAt(cursor.getPos()); cursor.updatePos(cursor.getPos() + 1); if (delim == PARAM_VALUE_SEPARATOR) { value = tokenParser.parseToken(s, cursor, QUERY_VALUE_SEPARATORS); if (!cursor.atEnd()) { cursor.updatePos(cursor.getPos() + 1); } } } if (!name.isEmpty()) { list.add(new BasicNameValuePair( PercentCodec.decode(name, charset, plusAsBlank), PercentCodec.decode(value, charset, plusAsBlank))); } } return list; } static List splitPath(final CharSequence s) { if (s == null) { return null; } final ParserCursor cursor = new ParserCursor(0, s.length()); // Skip leading separator if (cursor.atEnd()) { return new ArrayList<>(0); } if (PATH_SEPARATORS.get(s.charAt(cursor.getPos()))) { cursor.updatePos(cursor.getPos() + 1); } final List list = new ArrayList<>(); final StringBuilder buf = new StringBuilder(); for (;;) { if (cursor.atEnd()) { list.add(buf.toString()); break; } final char current = s.charAt(cursor.getPos()); if (PATH_SEPARATORS.get(current)) { list.add(buf.toString()); buf.setLength(0); } else { buf.append(current); } cursor.updatePos(cursor.getPos() + 1); } return list; } static List parsePath(final CharSequence s, final Charset charset) { if (s == null) { return null; } final List segments = splitPath(s); final List list = new ArrayList<>(segments.size()); for (final String segment: segments) { list.add(PercentCodec.decode(segment, charset)); } return list; } static void formatPath(final StringBuilder buf, final Iterable segments, final boolean rootless, final Charset charset) { int i = 0; for (final String segment : segments) { if (i > 0 || !rootless) { buf.append(PATH_SEPARATOR); } PercentCodec.encode(buf, segment, charset); i++; } } static void formatQuery(final StringBuilder buf, final Iterable params, final Charset charset, final boolean blankAsPlus) { int i = 0; for (final NameValuePair parameter : params) { if (i > 0) { buf.append(QUERY_PARAM_SEPARATOR); } PercentCodec.encode(buf, parameter.getName(), charset, blankAsPlus); if (parameter.getValue() != null) { buf.append(PARAM_VALUE_SEPARATOR); PercentCodec.encode(buf, parameter.getValue(), charset, blankAsPlus); } i++; } } /** * Builds a {@link URI} instance. */ public URI build() throws URISyntaxException { return new URI(buildString()); } private String buildString() { final StringBuilder sb = new StringBuilder(); if (this.scheme != null) { sb.append(this.scheme).append(':'); } if (this.encodedSchemeSpecificPart != null) { sb.append(this.encodedSchemeSpecificPart); } else { final boolean authoritySpecified; if (this.encodedAuthority != null) { sb.append("//").append(this.encodedAuthority); authoritySpecified = true; } else if (this.host != null) { sb.append("//"); if (this.encodedUserInfo != null) { sb.append(this.encodedUserInfo).append("@"); } else if (this.userInfo != null) { final int idx = this.userInfo.indexOf(':'); if (idx != -1) { PercentCodec.encode(sb, this.userInfo.substring(0, idx), this.charset); sb.append(':'); PercentCodec.encode(sb, this.userInfo.substring(idx + 1), this.charset); } else { PercentCodec.encode(sb, this.userInfo, this.charset); } sb.append("@"); } if (InetAddressUtils.isIPv6Address(this.host)) { sb.append("[").append(this.host).append("]"); } else { sb.append(PercentCodec.encode(this.host, this.charset)); } if (this.port >= 0) { sb.append(":").append(this.port); } authoritySpecified = true; } else { authoritySpecified = false; } if (this.encodedPath != null) { if (authoritySpecified && !TextUtils.isEmpty(this.encodedPath) && !this.encodedPath.startsWith("/")) { sb.append('/'); } sb.append(this.encodedPath); } else if (this.pathSegments != null) { formatPath(sb, this.pathSegments, !authoritySpecified && this.pathRootless, this.charset); } if (this.encodedQuery != null) { sb.append("?").append(this.encodedQuery); } else if (this.queryParams != null && !this.queryParams.isEmpty()) { sb.append("?"); formatQuery(sb, this.queryParams, this.charset, false); } else if (this.query != null) { sb.append("?"); PercentCodec.encode(sb, this.query, this.charset, PercentCodec.URIC, false); } } if (this.encodedFragment != null) { sb.append("#").append(this.encodedFragment); } else if (this.fragment != null) { sb.append("#"); PercentCodec.encode(sb, this.fragment, this.charset); } return sb.toString(); } private void digestURI(final URI uri, final Charset charset) { this.scheme = uri.getScheme(); this.encodedSchemeSpecificPart = uri.getRawSchemeSpecificPart(); this.encodedAuthority = uri.getRawAuthority(); final String uriHost = uri.getHost(); // URI.getHost incorrectly returns bracketed (encoded) IPv6 values. Brackets are an // encoding detail of the URI and not part of the host string. this.host = uriHost != null && InetAddressUtils.isIPv6URLBracketedAddress(uriHost) ? uriHost.substring(1, uriHost.length() - 1) : uriHost; this.port = uri.getPort(); this.encodedUserInfo = uri.getRawUserInfo(); this.userInfo = uri.getUserInfo(); if (this.encodedAuthority != null && this.host == null) { try { final URIAuthority uriAuthority = URIAuthority.parse(this.encodedAuthority); this.encodedUserInfo = uriAuthority.getUserInfo(); this.userInfo = PercentCodec.decode(uriAuthority.getUserInfo(), charset); this.host = PercentCodec.decode(uriAuthority.getHostName(), charset); this.port = uriAuthority.getPort(); } catch (final URISyntaxException ignore) { // ignore } } this.encodedPath = uri.getRawPath(); this.pathSegments = parsePath(uri.getRawPath(), charset); this.pathRootless = uri.getRawPath() == null || !uri.getRawPath().startsWith("/"); this.encodedQuery = uri.getRawQuery(); this.queryParams = parseQuery(uri.getRawQuery(), charset, false); this.encodedFragment = uri.getRawFragment(); this.fragment = uri.getFragment(); this.charset = charset; } /** * Sets URI scheme. * * @return this. */ public URIBuilder setScheme(final String scheme) { this.scheme = !TextUtils.isBlank(scheme) ? scheme : null; return this; } /** * Sets the URI scheme specific part. * * @param schemeSpecificPart * @return this. * @since 5.1 */ public URIBuilder setSchemeSpecificPart(final String schemeSpecificPart) { this.encodedSchemeSpecificPart = schemeSpecificPart; return this; } /** * Sets the URI scheme specific part and append a variable arguments list of NameValuePair instance(s) to this part. * * @param schemeSpecificPart * @param nvps Optional, can be null. Variable arguments list of NameValuePair query parameters to be reused by the specific scheme part * @return this. * @since 5.1 */ public URIBuilder setSchemeSpecificPart(final String schemeSpecificPart, final NameValuePair... nvps) { return setSchemeSpecificPart(schemeSpecificPart, nvps != null ? Arrays.asList(nvps) : null); } /** * Sets the URI scheme specific part and append a list of NameValuePair to this part. * * @param schemeSpecificPart * @param nvps Optional, can be null. List of query parameters to be reused by the specific scheme part * @return this. * @since 5.1 */ public URIBuilder setSchemeSpecificPart(final String schemeSpecificPart, final List nvps) { this.encodedSchemeSpecificPart = null; if (!TextUtils.isBlank(schemeSpecificPart)) { final StringBuilder sb = new StringBuilder(schemeSpecificPart); if (nvps != null && !nvps.isEmpty()) { sb.append("?"); formatQuery(sb, nvps, this.charset, false); } this.encodedSchemeSpecificPart = sb.toString(); } return this; } /** * Sets URI user info. The value is expected to be unescaped and may contain non ASCII * characters. * * @return this. */ public URIBuilder setUserInfo(final String userInfo) { this.userInfo = !TextUtils.isBlank(userInfo) ? userInfo : null; this.encodedSchemeSpecificPart = null; this.encodedAuthority = null; this.encodedUserInfo = null; return this; } /** * Sets URI user info as a combination of username and password. These values are expected to * be unescaped and may contain non ASCII characters. * * @return this. * * @deprecated The use of clear-text passwords in {@link URI}s has been deprecated and is strongly * discouraged. */ @Deprecated public URIBuilder setUserInfo(final String username, final String password) { return setUserInfo(username + ':' + password); } /** * Sets URI host. * * @return this. */ public URIBuilder setHost(final InetAddress host) { this.host = host != null ? host.getHostAddress() : null; this.encodedSchemeSpecificPart = null; this.encodedAuthority = null; return this; } /** * Sets URI host. The input value must not already be URI encoded, for example {@code ::1} is valid however * {@code [::1]} is not. It is dangerous to call {@code uriBuilder.setHost(uri.getHost())} due * to {@link URI#getHost()} returning URI encoded values. * * @return this. */ public URIBuilder setHost(final String host) { this.host = host; this.encodedSchemeSpecificPart = null; this.encodedAuthority = null; return this; } /** * Sets the scheme, host name, and port. * * @param httpHost the scheme, host name, and port. * @return this. */ public URIBuilder setHttpHost(final HttpHost httpHost) { setScheme(httpHost.getSchemeName()); setHost(httpHost.getHostName()); setPort(httpHost.getPort()); return this; } /** * Sets URI port. * * @return this. */ public URIBuilder setPort(final int port) { this.port = port < 0 ? -1 : port; this.encodedSchemeSpecificPart = null; this.encodedAuthority = null; return this; } /** * Sets URI path. The value is expected to be unescaped and may contain non ASCII characters. * * @return this. */ public URIBuilder setPath(final String path) { setPathSegments(path != null ? splitPath(path) : null); this.pathRootless = path != null && !path.startsWith("/"); return this; } /** * Appends path to URI. The value is expected to be unescaped and may contain non ASCII characters. * * @return this. */ public URIBuilder appendPath(final String path) { if (path != null) { appendPathSegments(splitPath(path)); } return this; } /** * Sets URI path. The value is expected to be unescaped and may contain non ASCII characters. * * @return this. */ public URIBuilder setPathSegments(final String... pathSegments) { return setPathSegments(Arrays.asList(pathSegments)); } /** * Appends segments URI path. The value is expected to be unescaped and may contain non ASCII characters. * * @return this. */ public URIBuilder appendPathSegments(final String... pathSegments) { return appendPathSegments(Arrays.asList(pathSegments)); } /** * Sets rootless URI path (the first segment does not start with a /). * The value is expected to be unescaped and may contain non ASCII characters. * * @return this. * * @since 5.1 */ public URIBuilder setPathSegmentsRootless(final String... pathSegments) { return setPathSegmentsRootless(Arrays.asList(pathSegments)); } /** * Sets URI path. The value is expected to be unescaped and may contain non ASCII characters. * * @return this. */ public URIBuilder setPathSegments(final List pathSegments) { this.pathSegments = pathSegments != null && !pathSegments.isEmpty() ? new ArrayList<>(pathSegments) : null; this.encodedSchemeSpecificPart = null; this.encodedPath = null; this.pathRootless = false; return this; } /** * Appends segments to URI path. The value is expected to be unescaped and may contain non ASCII characters. * * @return this. */ public URIBuilder appendPathSegments(final List pathSegments) { if (pathSegments != null && !pathSegments.isEmpty()) { if (this.pathSegments == null) { this.pathSegments = new ArrayList<>(); } this.pathSegments.addAll(pathSegments); this.encodedSchemeSpecificPart = null; this.encodedPath = null; } return this; } /** * Sets rootless URI path (the first segment does not start with a /). * The value is expected to be unescaped and may contain non ASCII characters. * * @return this. * * @since 5.1 */ public URIBuilder setPathSegmentsRootless(final List pathSegments) { this.pathSegments = pathSegments != null && !pathSegments.isEmpty() ? new ArrayList<>(pathSegments) : null; this.encodedSchemeSpecificPart = null; this.encodedPath = null; this.pathRootless = true; return this; } /** * Removes URI query. * * @return this. */ public URIBuilder removeQuery() { this.queryParams = null; this.query = null; this.encodedQuery = null; this.encodedSchemeSpecificPart = null; return this; } /** * Sets URI query parameters. The parameter name / values are expected to be unescaped * and may contain non ASCII characters. *

* Please note query parameters and custom query component are mutually exclusive. This method * will remove custom query if present. *

* * @return this. */ public URIBuilder setParameters(final List nameValuePairs) { if (this.queryParams == null) { this.queryParams = new ArrayList<>(); } else { this.queryParams.clear(); } if (nameValuePairs != null) { this.queryParams.addAll(nameValuePairs); } this.encodedQuery = null; this.encodedSchemeSpecificPart = null; this.query = null; return this; } /** * Adds URI query parameters. The parameter name / values are expected to be unescaped * and may contain non ASCII characters. *

* Please note query parameters and custom query component are mutually exclusive. This method * will remove custom query if present. *

* * @return this. */ public URIBuilder addParameters(final List nameValuePairs) { if (this.queryParams == null) { this.queryParams = new ArrayList<>(); } if (nameValuePairs != null) { this.queryParams.addAll(nameValuePairs); } this.encodedQuery = null; this.encodedSchemeSpecificPart = null; this.query = null; return this; } /** * Sets URI query parameters. The parameter name / values are expected to be unescaped * and may contain non ASCII characters. *

* Please note query parameters and custom query component are mutually exclusive. This method * will remove custom query if present. *

* * @return this. */ public URIBuilder setParameters(final NameValuePair... nameValuePairs) { if (this.queryParams == null) { this.queryParams = new ArrayList<>(); } else { this.queryParams.clear(); } if (nameValuePairs != null) { Collections.addAll(this.queryParams, nameValuePairs); } this.encodedQuery = null; this.encodedSchemeSpecificPart = null; this.query = null; return this; } /** * Adds parameter to URI query. The parameter name and value are expected to be unescaped * and may contain non ASCII characters. *

* Please note query parameters and custom query component are mutually exclusive. This method * will remove custom query if present. *

* * @return this. */ public URIBuilder addParameter(final String param, final String value) { return addParameter(new BasicNameValuePair(param, value)); } /** * Adds parameter to URI query. The parameter name and value are expected to be unescaped * and may contain non ASCII characters. *

* Please note query parameters and custom query component are mutually exclusive. This method * will remove custom query if present. *

* * @return this. * @since 5.2 */ public URIBuilder addParameter(final NameValuePair nameValuePair) { if (this.queryParams == null) { this.queryParams = new ArrayList<>(); } if (nameValuePair != null) { this.queryParams.add(nameValuePair); } this.encodedQuery = null; this.encodedSchemeSpecificPart = null; this.query = null; return this; } /** * Removes parameter of URI query if set. The parameter name is expected to be unescaped and may * contain non ASCII characters. *

* Please note query parameters and custom query component are mutually exclusive. This method * will remove custom query if present, even when no parameter was actually removed. *

* * @return this. * @since 5.2 */ public URIBuilder removeParameter(final String param) { Args.notNull(param, "param"); if (this.queryParams != null && !this.queryParams.isEmpty()) { this.queryParams.removeIf(nvp -> nvp.getName().equals(param)); } this.encodedQuery = null; this.encodedSchemeSpecificPart = null; this.query = null; return this; } /** * Sets parameter of URI query overriding existing value if set. The parameter name and value * are expected to be unescaped and may contain non ASCII characters. *

* Please note query parameters and custom query component are mutually exclusive. This method * will remove custom query if present. *

* * @return this. */ public URIBuilder setParameter(final String param, final String value) { if (this.queryParams == null) { this.queryParams = new ArrayList<>(); } if (!this.queryParams.isEmpty()) { this.queryParams.removeIf(nvp -> nvp.getName().equals(param)); } this.queryParams.add(new BasicNameValuePair(param, value)); this.encodedQuery = null; this.encodedSchemeSpecificPart = null; this.query = null; return this; } /** * Clears URI query parameters. * * @return this. */ public URIBuilder clearParameters() { this.queryParams = null; this.encodedQuery = null; this.encodedSchemeSpecificPart = null; return this; } /** * Sets custom URI query. The value is expected to be unescaped and may contain non ASCII * characters. *

* Please note query parameters and custom query component are mutually exclusive. This method * will remove query parameters if present. *

* * @return this. */ public URIBuilder setCustomQuery(final String query) { this.query = !TextUtils.isBlank(query) ? query : null; this.encodedQuery = null; this.encodedSchemeSpecificPart = null; this.queryParams = null; return this; } /** * Sets URI fragment. The value is expected to be unescaped and may contain non ASCII * characters. * * @return this. */ public URIBuilder setFragment(final String fragment) { this.fragment = !TextUtils.isBlank(fragment) ? fragment : null; this.encodedFragment = null; return this; } /** * Tests whether the URI is absolute. * * @return whether the URI is absolute. */ public boolean isAbsolute() { return this.scheme != null; } /** * Tests whether the URI is opaque. * * @return whether the URI is opaque. */ public boolean isOpaque() { return this.pathSegments == null && this.encodedPath == null; } /** * Gets the scheme. * * @return the scheme. */ public String getScheme() { return this.scheme; } /** * Gets the scheme specific part. * * @return String * @since 5.1 */ public String getSchemeSpecificPart() { return this.encodedSchemeSpecificPart; } /** * Gets the user info. * * @return the user info. */ public String getUserInfo() { return this.userInfo; } /** * Gets the host portion of the {@link URI}. This method returns unencoded IPv6 addresses (without brackets). * This behavior differs from values returned by {@link URI#getHost()}. * * @return The host portion of the URI. */ public String getHost() { return this.host; } /** * Gets the port. * * @return the port. */ public int getPort() { return this.port; } /** * Tests whether the path is empty. * * @return whether the path is empty. */ public boolean isPathEmpty() { return (this.pathSegments == null || this.pathSegments.isEmpty()) && (this.encodedPath == null || this.encodedPath.isEmpty()); } /** * Gets the path segments. * * @return the path segments. */ public List getPathSegments() { return this.pathSegments != null ? new ArrayList<>(this.pathSegments) : new ArrayList<>(); } /** * Gets the path. * * @return the path. */ public String getPath() { if (this.pathSegments == null) { return null; } final StringBuilder result = new StringBuilder(); for (final String segment : this.pathSegments) { result.append('/').append(segment); } return result.toString(); } /** * Tests whether the query is empty. * * @return whether the query is empty. */ public boolean isQueryEmpty() { return (this.queryParams == null || this.queryParams.isEmpty()) && this.encodedQuery == null; } /** * Gets the query parameters as a List. * * @return the query parameters as a List. */ public List getQueryParams() { return this.queryParams != null ? new ArrayList<>(this.queryParams) : new ArrayList<>(); } /** * Gets the first {@link NameValuePair} for a given name. * * @param name the name * @return the first named {@link NameValuePair} or null if not found. * @since 5.2 */ public NameValuePair getFirstQueryParam(final String name) { return queryParams.stream().filter(e -> name.equals(e.getName())).findFirst().orElse(null); } /** * Gets the fragments. * * @return the fragments. */ public String getFragment() { return this.fragment; } /** * Normalizes syntax of URI components if the URI is considered non-opaque * (the path component has a root): *
    *
  • characters of scheme and host components are converted to lower case
  • *
  • dot segments of the path component are removed if the path has a root
  • *
  • percent encoding of all components is normalized
  • *
* * @since 5.1 */ public URIBuilder normalizeSyntax() { final String scheme = this.scheme; if (scheme != null) { this.scheme = TextUtils.toLowerCase(scheme); } if (this.pathRootless) { return this; } // Force Percent-Encoding normalization this.encodedSchemeSpecificPart = null; this.encodedAuthority = null; this.encodedUserInfo = null; this.encodedPath = null; this.encodedQuery = null; this.encodedFragment = null; final String host = this.host; if (host != null) { this.host = TextUtils.toLowerCase(host); } if (this.pathSegments != null) { final List inputSegments = this.pathSegments; if (!inputSegments.isEmpty()) { final LinkedList outputSegments = new LinkedList<>(); for (final String inputSegment : inputSegments) { if (!inputSegment.isEmpty() && !".".equals(inputSegment)) { if ("..".equals(inputSegment)) { if (!outputSegments.isEmpty()) { outputSegments.removeLast(); } } else { outputSegments.addLast(inputSegment); } } } if (!inputSegments.isEmpty()) { final String lastSegment = inputSegments.get(inputSegments.size() - 1); if (lastSegment.isEmpty()) { outputSegments.addLast(""); } } this.pathSegments = outputSegments; } else { this.pathSegments = Collections.singletonList(""); } } return this; } /** * Converts this instance to a URI string. * * @return this instance to a URI string. */ @Override public String toString() { return buildString(); } } httpcore5/src/main/java/org/apache/hc/core5/net/URIAuthority.java0100664 0000000 0000000 00000014061 14403631147 023605 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import java.io.Serializable; import java.net.URISyntaxException; import java.util.Objects; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.LangUtils; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Tokenizer; /** * Represents authority component of request {@link java.net.URI}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class URIAuthority implements NamedEndpoint, Serializable { private static final long serialVersionUID = 1L; private final String userInfo; private final Host host; static URIAuthority parse(final CharSequence s, final Tokenizer.Cursor cursor) throws URISyntaxException { final Tokenizer tokenizer = Tokenizer.INSTANCE; String userInfo = null; final int initPos = cursor.getPos(); final String token = tokenizer.parseContent(s, cursor, URISupport.HOST_SEPARATORS); if (!cursor.atEnd() && s.charAt(cursor.getPos()) == '@') { cursor.updatePos(cursor.getPos() + 1); if (!TextUtils.isBlank(token)) { userInfo = token; } } else { //Rewind cursor.updatePos(initPos); } final Host host = Host.parse(s, cursor); return new URIAuthority(userInfo, host); } static URIAuthority parse(final CharSequence s) throws URISyntaxException { final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); return parse(s, cursor); } static void format(final StringBuilder buf, final URIAuthority uriAuthority) { if (uriAuthority.getUserInfo() != null) { buf.append(uriAuthority.getUserInfo()); buf.append("@"); } Host.format(buf, uriAuthority); } static String format(final URIAuthority uriAuthority) { final StringBuilder buf = new StringBuilder(); format(buf, uriAuthority); return buf.toString(); } /** * Constructs a new instance. * * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. {@code -1} indicates the scheme default port. */ public URIAuthority(final String userInfo, final String hostname, final int port) { super(); this.userInfo = userInfo; this.host = new Host(hostname, port); } public URIAuthority(final String hostname, final int port) { this(null, hostname, port); } /** * @since 5.2 */ public URIAuthority(final String userInfo, final Host host) { super(); Args.notNull(host, "Host"); this.userInfo = userInfo; this.host = host; } /** * @since 5.2 */ public URIAuthority(final Host host) { this(null, host); } /** * @since 5.2 */ public URIAuthority(final String userInfo, final NamedEndpoint endpoint) { super(); Args.notNull(endpoint, "Endpoint"); this.userInfo = userInfo; this.host = new Host(endpoint.getHostName(), endpoint.getPort()); } public URIAuthority(final NamedEndpoint namedEndpoint) { this(null, namedEndpoint); } /** * Creates a {@code URIAuthority} instance from a string. Text may not contain any blanks. */ public static URIAuthority create(final String s) throws URISyntaxException { if (TextUtils.isBlank(s)) { return null; } final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); final URIAuthority uriAuthority = parse(s, cursor); if (!cursor.atEnd()) { throw URISupport.createException(s, cursor, "Unexpected content"); } return uriAuthority; } public URIAuthority(final String hostname) { this(null, hostname, -1); } public String getUserInfo() { return userInfo; } @Override public String getHostName() { return host.getHostName(); } @Override public int getPort() { return host.getPort(); } @Override public String toString() { return format(this); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof URIAuthority) { final URIAuthority that = (URIAuthority) obj; return Objects.equals(this.userInfo, that.userInfo) && Objects.equals(this.host, that.host); } return false; } @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, userInfo); hash = LangUtils.hashCode(hash, host); return hash; } } httpcore5/src/main/java/org/apache/hc/core5/reactor/0040775 0000000 0000000 00000000000 14443061705 021245 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/reactor/package-info.java0100664 0000000 0000000 00000002636 14245617503 024443 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Event driven network communication APIs and components loosely based on Doug Lea's reactor pattern. */ package org.apache.hc.core5.reactor; httpcore5/src/main/java/org/apache/hc/core5/reactor/ConnectionAcceptor.java0100664 0000000 0000000 00000006304 14403631147 025667 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.net.SocketAddress; import java.util.Set; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.FutureCallback; /** * Non-blocking connection acceptor. * * @since 5.0 */ public interface ConnectionAcceptor { /** * Opens a new listener endpoint with the given socket address. Once * the endpoint is fully initialized it starts accepting incoming * connections and propagates I/O activity notifications to the I/O event * dispatcher. * * @param address the socket address to listen on. * @param attachment the attachment object. * @param callback the result callback. * @return listener endpoint. * * @since 5.2 */ default Future listen( SocketAddress address, Object attachment, FutureCallback callback) { return listen(address, callback); } /** * Opens a new listener endpoint with the given socket address. Once * the endpoint is fully initialized it starts accepting incoming * connections and propagates I/O activity notifications to the I/O event * dispatcher. * * @param address the socket address to listen on. * @param callback the result callback. * @return listener endpoint. */ Future listen(SocketAddress address, FutureCallback callback); /** * Suspends the I/O reactor preventing it from accepting new connections on * all active endpoints. * * @throws IOException in case of an I/O error. */ void pause() throws IOException; /** * Resumes the I/O reactor restoring its ability to accept incoming * connections on all active endpoints. * * @throws IOException in case of an I/O error. */ void resume() throws IOException; /** * Returns a set of endpoints for this I/O reactor. * * @return set of endpoints. */ Set getEndpoints(); } httpcore5/src/main/java/org/apache/hc/core5/reactor/SingleCoreListeningIOReactor.java0100664 0000000 0000000 00000021340 14403631147 027563 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.net.BindException; import java.net.ServerSocket; import java.net.SocketAddress; import java.nio.channels.CancelledKeyException; import java.nio.channels.SelectionKey; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.HashSet; import java.util.Iterator; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.io.Closer; class SingleCoreListeningIOReactor extends AbstractSingleCoreIOReactor implements ConnectionAcceptor { private final IOReactorConfig reactorConfig; private final Callback callback; private final Queue requestQueue; private final ConcurrentMap endpoints; private final AtomicBoolean paused; private final long selectTimeoutMillis; SingleCoreListeningIOReactor( final Callback exceptionCallback, final IOReactorConfig ioReactorConfig, final Callback callback) { super(exceptionCallback); this.reactorConfig = ioReactorConfig != null ? ioReactorConfig : IOReactorConfig.DEFAULT; this.callback = callback; this.requestQueue = new ConcurrentLinkedQueue<>(); this.endpoints = new ConcurrentHashMap<>(); this.paused = new AtomicBoolean(false); this.selectTimeoutMillis = this.reactorConfig.getSelectInterval().toMilliseconds(); } @Override void doTerminate() { ListenerEndpointRequest request; while ((request = this.requestQueue.poll()) != null) { request.cancel(); } } @Override protected final void doExecute() throws IOException { while (!Thread.currentThread().isInterrupted()) { if (getStatus() != IOReactorStatus.ACTIVE) { break; } final int readyCount = this.selector.select(this.selectTimeoutMillis); if (getStatus() != IOReactorStatus.ACTIVE) { break; } processEvents(readyCount); } } private void processEvents(final int readyCount) throws IOException { if (!this.paused.get()) { processSessionRequests(); } if (readyCount > 0) { final Set selectedKeys = this.selector.selectedKeys(); for (final SelectionKey key : selectedKeys) { processEvent(key); } selectedKeys.clear(); } } private void processEvent(final SelectionKey key) throws IOException { try { if (key.isAcceptable()) { final ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); for (;;) { final SocketChannel socketChannel = serverChannel.accept(); if (socketChannel == null) { break; } final ListenerEndpointRequest endpointRequest = (ListenerEndpointRequest) key.attachment(); this.callback.execute(new ChannelEntry(socketChannel, endpointRequest.attachment)); } } } catch (final CancelledKeyException ex) { final ListenerEndpointImpl endpoint = (ListenerEndpointImpl) key.attachment(); this.endpoints.remove(endpoint); key.attach(null); } } @Override public Future listen( final SocketAddress address, final Object attachment, final FutureCallback callback) { if (getStatus().compareTo(IOReactorStatus.SHUTTING_DOWN) >= 0) { throw new IOReactorShutdownException("I/O reactor has been shut down"); } final BasicFuture future = new BasicFuture<>(callback); this.requestQueue.add(new ListenerEndpointRequest(address, attachment, future)); this.selector.wakeup(); return future; } @Override public Future listen(final SocketAddress address, final FutureCallback callback) { return listen(address, null, callback); } private void processSessionRequests() throws IOException { ListenerEndpointRequest request; while ((request = this.requestQueue.poll()) != null) { if (request.isCancelled()) { continue; } final SocketAddress address = request.address; final ServerSocketChannel serverChannel = ServerSocketChannel.open(); try { final ServerSocket socket = serverChannel.socket(); socket.setReuseAddress(this.reactorConfig.isSoReuseAddress()); if (this.reactorConfig.getRcvBufSize() > 0) { socket.setReceiveBufferSize(this.reactorConfig.getRcvBufSize()); } serverChannel.configureBlocking(false); try { socket.bind(address, this.reactorConfig.getBacklogSize()); } catch (final BindException ex) { final BindException detailedEx = new BindException( String.format("Socket bind failure for socket %s, address=%s, BacklogSize=%d: %s", socket, address, this.reactorConfig.getBacklogSize(), ex)); detailedEx.setStackTrace(ex.getStackTrace()); throw detailedEx; } final SelectionKey key = serverChannel.register(this.selector, SelectionKey.OP_ACCEPT); key.attach(request); final ListenerEndpointImpl endpoint = new ListenerEndpointImpl(key, request.attachment, socket.getLocalSocketAddress()); this.endpoints.put(endpoint, Boolean.TRUE); request.completed(endpoint); } catch (final IOException ex) { Closer.closeQuietly(serverChannel); request.failed(ex); } } } @Override public Set getEndpoints() { final Set set = new HashSet<>(); final Iterator it = this.endpoints.keySet().iterator(); while (it.hasNext()) { final ListenerEndpoint endpoint = it.next(); if (!endpoint.isClosed()) { set.add(endpoint); } else { it.remove(); } } return set; } @Override public void pause() throws IOException { if (paused.compareAndSet(false, true)) { final Iterator it = this.endpoints.keySet().iterator(); while (it.hasNext()) { final ListenerEndpointImpl endpoint = it.next(); if (!endpoint.isClosed()) { endpoint.close(); this.requestQueue.add(new ListenerEndpointRequest(endpoint.address, endpoint.attachment, null)); } it.remove(); } } } @Override public void resume() throws IOException { if (paused.compareAndSet(true, false)) { this.selector.wakeup(); } } } httpcore5/src/main/java/org/apache/hc/core5/reactor/AbstractSingleCoreIOReactor.java0100664 0000000 0000000 00000014642 14403631147 027401 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.Closeable; import java.io.IOException; import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; abstract class AbstractSingleCoreIOReactor implements IOReactor { private final Callback exceptionCallback; private final AtomicReference status; private final AtomicBoolean terminated; private final Object shutdownMutex; final Selector selector; AbstractSingleCoreIOReactor(final Callback exceptionCallback) { super(); this.exceptionCallback = exceptionCallback; this.shutdownMutex = new Object(); this.status = new AtomicReference<>(IOReactorStatus.INACTIVE); this.terminated = new AtomicBoolean(); try { this.selector = Selector.open(); } catch (final IOException ex) { throw new IllegalStateException("Unexpected failure opening I/O selector", ex); } } @Override public final IOReactorStatus getStatus() { return this.status.get(); } void logException(final Exception ex) { if (exceptionCallback != null) { exceptionCallback.execute(ex); } } abstract void doExecute() throws IOException; abstract void doTerminate() throws IOException; public void execute() { if (this.status.compareAndSet(IOReactorStatus.INACTIVE, IOReactorStatus.ACTIVE)) { try { doExecute(); } catch (final ClosedSelectorException ignore) { // ignore } catch (final Exception ex) { logException(ex); } finally { try { doTerminate(); } catch (final Exception ex) { logException(ex); } finally { close(CloseMode.IMMEDIATE); } } } } @Override public final void awaitShutdown(final TimeValue waitTime) throws InterruptedException { Args.notNull(waitTime, "Wait time"); final long deadline = System.currentTimeMillis() + waitTime.toMilliseconds(); long remaining = waitTime.toMilliseconds(); synchronized (this.shutdownMutex) { while (this.status.get().compareTo(IOReactorStatus.SHUT_DOWN) < 0) { this.shutdownMutex.wait(remaining); remaining = deadline - System.currentTimeMillis(); if (remaining <= 0) { return; } } } } @Override public final void initiateShutdown() { if (this.status.compareAndSet(IOReactorStatus.INACTIVE, IOReactorStatus.SHUT_DOWN)) { synchronized (this.shutdownMutex) { this.shutdownMutex.notifyAll(); } } else if (this.status.compareAndSet(IOReactorStatus.ACTIVE, IOReactorStatus.SHUTTING_DOWN)) { this.selector.wakeup(); } } @Override public final void close(final CloseMode closeMode) { close(closeMode, Timeout.ofSeconds(5)); } /** * Shuts down the I/O reactor either gracefully or immediately. * During graceful shutdown individual I/O sessions should be * informed about imminent termination and be given a grace period * to complete the ongoing I/O sessions. During immediate shutdown * all ongoing I/O sessions get aborted immediately. * * @param closeMode How to close the IO reactor. * @param timeout How long to wait for the IO reactor to close gracefully. * @since 5.2 */ public void close(final CloseMode closeMode, final Timeout timeout) { if (closeMode == CloseMode.GRACEFUL) { initiateShutdown(); try { awaitShutdown(timeout); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } } this.status.set(IOReactorStatus.SHUT_DOWN); if (terminated.compareAndSet(false, true)) { try { final Set keys = this.selector.keys(); for (final SelectionKey key : keys) { try { Closer.close((Closeable) key.attachment()); } catch (final IOException ex) { logException(ex); } key.channel().close(); } selector.close(); } catch (final Exception ex) { logException(ex); } } synchronized (this.shutdownMutex) { this.shutdownMutex.notifyAll(); } } @Override public final void close() { close(CloseMode.GRACEFUL); } @Override public String toString() { return super.toString() + " [status=" + status + "]"; } } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOReactorStatus.java0100664 0000000 0000000 00000003167 14245617503 025152 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; /** * IOReactorStatus represents an internal status of an I/O reactor. * * @since 4.0 */ public enum IOReactorStatus { /** * The reactor is inactive / has not been started */ INACTIVE, /** * The reactor is active / processing I/O events. */ ACTIVE, /** * The reactor is shutting down. */ SHUTTING_DOWN, /** * The reactor has shut down. */ SHUT_DOWN } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOWorkers.java0100664 0000000 0000000 00000006071 14245617503 024000 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.util.concurrent.atomic.AtomicInteger; final class IOWorkers { interface Selector { SingleCoreIOReactor next(); } static Selector newSelector(final SingleCoreIOReactor[] dispatchers) { return isPowerOfTwo(dispatchers.length) ? new PowerOfTwoSelector(dispatchers) : new GenericSelector(dispatchers); } private static boolean isPowerOfTwo(final int val) { return (val & -val) == val; } private static void validate(final SingleCoreIOReactor dispatcher) { if (dispatcher.getStatus() == IOReactorStatus.SHUT_DOWN) { throw new IOReactorShutdownException("I/O reactor has been shut down"); } } private static final class PowerOfTwoSelector implements Selector { private final AtomicInteger idx = new AtomicInteger(0); private final SingleCoreIOReactor[] dispatchers; PowerOfTwoSelector(final SingleCoreIOReactor[] dispatchers) { this.dispatchers = dispatchers; } @Override public SingleCoreIOReactor next() { final SingleCoreIOReactor dispatcher = dispatchers[idx.getAndIncrement() & (dispatchers.length - 1)]; validate(dispatcher); return dispatcher; } } private static final class GenericSelector implements Selector { private final AtomicInteger idx = new AtomicInteger(0); private final SingleCoreIOReactor[] dispatchers; GenericSelector(final SingleCoreIOReactor[] dispatchers) { this.dispatchers = dispatchers; } @Override public SingleCoreIOReactor next() { final SingleCoreIOReactor dispatcher = dispatchers[(idx.getAndIncrement() & Integer.MAX_VALUE) % dispatchers.length]; validate(dispatcher); return dispatcher; } } } httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalConnectChannel.java0100664 0000000 0000000 00000010622 14403631147 026464 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.io.SocketTimeoutExceptionFactory; import org.apache.hc.core5.util.Timeout; final class InternalConnectChannel extends InternalChannel { private final SelectionKey key; private final SocketChannel socketChannel; private final IOSessionRequest sessionRequest; private final InternalDataChannel dataChannel; private final IOEventHandlerFactory eventHandlerFactory; private final IOReactorConfig reactorConfig; private final long creationTimeMillis; InternalConnectChannel( final SelectionKey key, final SocketChannel socketChannel, final IOSessionRequest sessionRequest, final InternalDataChannel dataChannel, final IOEventHandlerFactory eventHandlerFactory, final IOReactorConfig reactorConfig) { super(); this.key = key; this.socketChannel = socketChannel; this.sessionRequest = sessionRequest; this.dataChannel = dataChannel; this.eventHandlerFactory = eventHandlerFactory; this.reactorConfig = reactorConfig; this.creationTimeMillis = System.currentTimeMillis(); } @Override void onIOEvent(final int readyOps) throws IOException { if ((readyOps & SelectionKey.OP_CONNECT) != 0) { if (socketChannel.isConnectionPending()) { socketChannel.finishConnect(); } //check out connectTimeout final long now = System.currentTimeMillis(); if (checkTimeout(now)) { key.attach(dataChannel); if (reactorConfig.getSocksProxyAddress() == null) { dataChannel.upgrade(eventHandlerFactory.createHandler(dataChannel, sessionRequest.attachment)); sessionRequest.completed(dataChannel); dataChannel.handleIOEvent(SelectionKey.OP_CONNECT); } else { final IOEventHandler ioEventHandler = new SocksProxyProtocolHandler( dataChannel, sessionRequest, eventHandlerFactory, reactorConfig); dataChannel.upgrade(ioEventHandler); ioEventHandler.connected(dataChannel); } } } } @Override Timeout getTimeout() { return sessionRequest.timeout; } @Override long getLastEventTime() { return creationTimeMillis; } @Override void onTimeout(final Timeout timeout) throws IOException { sessionRequest.failed(SocketTimeoutExceptionFactory.create(timeout)); close(); } @Override void onException(final Exception cause) { sessionRequest.failed(cause); } @Override public void close() throws IOException { key.cancel(); socketChannel.close(); } @Override public void close(final CloseMode closeMode) { key.cancel(); Closer.closeQuietly(socketChannel); } @Override public String toString() { return sessionRequest.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/reactor/EventMask.java0100664 0000000 0000000 00000003360 14245617503 024007 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.nio.channels.SelectionKey; /** * Type of I/O event notifications I/O sessions can declare interest in. * * @since 4.0 */ public final class EventMask { private EventMask() { // no instances. } /** * Interest in data input. */ public static final int READ = SelectionKey.OP_READ; /** * Interest in data output. */ public static final int WRITE = SelectionKey.OP_WRITE; /** * Interest in data input/output. */ public static final int READ_WRITE = READ | WRITE; } httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java0100664 0000000 0000000 00000035672 14403631147 025760 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.channels.SelectionKey; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.SSLIOSession; import org.apache.hc.core5.reactor.ssl.SSLMode; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Timeout; final class InternalDataChannel extends InternalChannel implements ProtocolIOSession { private final IOSession ioSession; private final NamedEndpoint initialEndpoint; private final Decorator ioSessionDecorator; private final IOSessionListener sessionListener; private final Queue closedSessions; private final AtomicReference tlsSessionRef; private final AtomicReference currentSessionRef; private final AtomicReference eventHandlerRef; private final ConcurrentMap protocolUpgradeHandlerMap; private final AtomicBoolean closed; InternalDataChannel( final IOSession ioSession, final NamedEndpoint initialEndpoint, final Decorator ioSessionDecorator, final IOSessionListener sessionListener, final Queue closedSessions) { this.ioSession = ioSession; this.initialEndpoint = initialEndpoint; this.closedSessions = closedSessions; this.ioSessionDecorator = ioSessionDecorator; this.sessionListener = sessionListener; this.tlsSessionRef = new AtomicReference<>(); this.currentSessionRef = new AtomicReference<>( ioSessionDecorator != null ? ioSessionDecorator.decorate(ioSession) : ioSession); this.eventHandlerRef = new AtomicReference<>(); this.protocolUpgradeHandlerMap = new ConcurrentHashMap<>(); this.closed = new AtomicBoolean(false); } @Override public String getId() { return ioSession.getId(); } @Override public NamedEndpoint getInitialEndpoint() { return initialEndpoint; } @Override public IOEventHandler getHandler() { return eventHandlerRef.get(); } @Override public void upgrade(final IOEventHandler handler) { final IOSession currentSession = currentSessionRef.get(); currentSession.upgrade(handler); eventHandlerRef.set(handler); } private IOEventHandler ensureHandler(final IOSession session) { final IOEventHandler handler = session.getHandler(); Asserts.notNull(handler, "IO event handler"); return handler; } @Override void onIOEvent(final int readyOps) throws IOException { if ((readyOps & SelectionKey.OP_CONNECT) != 0) { final IOSession currentSession = currentSessionRef.get(); currentSession.clearEvent(SelectionKey.OP_CONNECT); if (tlsSessionRef.get() == null) { if (sessionListener != null) { sessionListener.connected(currentSession); } final IOEventHandler handler = ensureHandler(currentSession); handler.connected(currentSession); } } if ((readyOps & SelectionKey.OP_READ) != 0) { final IOSession currentSession = currentSessionRef.get(); currentSession.updateReadTime(); if (sessionListener != null) { sessionListener.inputReady(currentSession); } final IOEventHandler handler = ensureHandler(currentSession); handler.inputReady(currentSession, null); } if ((readyOps & SelectionKey.OP_WRITE) != 0 || (ioSession.getEventMask() & SelectionKey.OP_WRITE) != 0) { final IOSession currentSession = currentSessionRef.get(); currentSession.updateWriteTime(); if (sessionListener != null) { sessionListener.outputReady(currentSession); } final IOEventHandler handler = ensureHandler(currentSession); handler.outputReady(currentSession); } } @Override Timeout getTimeout() { final IOSession currentSession = currentSessionRef.get(); return currentSession.getSocketTimeout(); } @Override void onTimeout(final Timeout timeout) throws IOException { final IOSession currentSession = currentSessionRef.get(); if (sessionListener != null) { sessionListener.timeout(currentSession); } final IOEventHandler handler = ensureHandler(currentSession); handler.timeout(currentSession, timeout); } @Override void onException(final Exception cause) { final IOSession currentSession = currentSessionRef.get(); if (sessionListener != null) { sessionListener.exception(currentSession, cause); } final IOEventHandler handler = currentSession.getHandler(); if (handler != null) { handler.exception(currentSession, cause); } } void onTLSSessionStart(final SSLIOSession sslSession) { final IOSession currentSession = currentSessionRef.get(); if (sessionListener != null) { sessionListener.connected(currentSession); } } void onTLSSessionEnd(final SSLIOSession sslSession) { if (closed.compareAndSet(false, true)) { closedSessions.add(this); } } void disconnected() { final IOSession currentSession = currentSessionRef.get(); if (sessionListener != null) { sessionListener.disconnected(currentSession); } final IOEventHandler handler = currentSession.getHandler(); if (handler != null) { handler.disconnected(currentSession); } } @Override public void startTls( final SSLContext sslContext, final NamedEndpoint endpoint, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier, final Timeout handshakeTimeout) { startTls(sslContext, endpoint, sslBufferMode, initializer, verifier, handshakeTimeout, null); } @Override public void startTls( final SSLContext sslContext, final NamedEndpoint endpoint, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier, final Timeout handshakeTimeout, final FutureCallback callback) { final SSLIOSession sslioSession = new SSLIOSession( endpoint != null ? endpoint : initialEndpoint, ioSession, initialEndpoint != null ? SSLMode.CLIENT : SSLMode.SERVER, sslContext, sslBufferMode, initializer, verifier, handshakeTimeout, this::onTLSSessionStart, this::onTLSSessionEnd, new CallbackContribution(callback) { @Override public void completed(final SSLSession sslSession) { if (callback != null) { callback.completed(InternalDataChannel.this); } } }); if (tlsSessionRef.compareAndSet(null, sslioSession)) { currentSessionRef.set(ioSessionDecorator != null ? ioSessionDecorator.decorate(sslioSession) : sslioSession); } else { throw new IllegalStateException("TLS already activated"); } try { if (sessionListener != null) { sessionListener.startTls(sslioSession); } sslioSession.beginHandshake(this); } catch (final Exception ex) { onException(ex); } } @SuppressWarnings("resource") @Override public TlsDetails getTlsDetails() { final SSLIOSession sslIoSession = tlsSessionRef.get(); return sslIoSession != null ? sslIoSession.getTlsDetails() : null; } @Override public Lock getLock() { return ioSession.getLock(); } @Override public void close() { close(CloseMode.GRACEFUL); } @Override public void close(final CloseMode closeMode) { final IOSession currentSession = currentSessionRef.get(); if (closeMode == CloseMode.IMMEDIATE) { closed.set(true); currentSession.close(closeMode); } else { if (closed.compareAndSet(false, true)) { try { currentSession.close(closeMode); } finally { closedSessions.add(this); } } } } @Override public IOSession.Status getStatus() { final IOSession currentSession = currentSessionRef.get(); return currentSession.getStatus(); } @Override public boolean isOpen() { final IOSession currentSession = currentSessionRef.get(); return currentSession.isOpen(); } @Override public void enqueue(final Command command, final Command.Priority priority) { final IOSession currentSession = currentSessionRef.get(); currentSession.enqueue(command, priority); } @Override public boolean hasCommands() { final IOSession currentSession = currentSessionRef.get(); return currentSession.hasCommands(); } @Override public Command poll() { final IOSession currentSession = currentSessionRef.get(); return currentSession.poll(); } @Override public ByteChannel channel() { final IOSession currentSession = currentSessionRef.get(); return currentSession.channel(); } @Override public SocketAddress getRemoteAddress() { return ioSession.getRemoteAddress(); } @Override public SocketAddress getLocalAddress() { return ioSession.getLocalAddress(); } @Override public int getEventMask() { final IOSession currentSession = currentSessionRef.get(); return currentSession.getEventMask(); } @Override public void setEventMask(final int ops) { final IOSession currentSession = currentSessionRef.get(); currentSession.setEventMask(ops); } @Override public void setEvent(final int op) { final IOSession currentSession = currentSessionRef.get(); currentSession.setEvent(op); } @Override public void clearEvent(final int op) { final IOSession currentSession = currentSessionRef.get(); currentSession.clearEvent(op); } @Override public Timeout getSocketTimeout() { return ioSession.getSocketTimeout(); } @Override public void setSocketTimeout(final Timeout timeout) { ioSession.setSocketTimeout(timeout); } @Override public int read(final ByteBuffer dst) throws IOException { final IOSession currentSession = currentSessionRef.get(); return currentSession.read(dst); } @Override public int write(final ByteBuffer src) throws IOException { final IOSession currentSession = currentSessionRef.get(); return currentSession.write(src); } @Override public void updateReadTime() { ioSession.updateReadTime(); } @Override public void updateWriteTime() { ioSession.updateWriteTime(); } @Override public long getLastReadTime() { return ioSession.getLastReadTime(); } @Override public long getLastWriteTime() { return ioSession.getLastWriteTime(); } @Override public long getLastEventTime() { return ioSession.getLastEventTime(); } @Override public void switchProtocol(final String protocolId, final FutureCallback callback) { Args.notEmpty(protocolId, "Application protocol ID"); final ProtocolUpgradeHandler upgradeHandler = protocolUpgradeHandlerMap.get(TextUtils.toLowerCase(protocolId)); if (upgradeHandler != null) { upgradeHandler.upgrade(this, callback); } else { throw new IllegalStateException("Unsupported protocol: " + protocolId); } } @Override public void registerProtocol(final String protocolId, final ProtocolUpgradeHandler upgradeHandler) { Args.notEmpty(protocolId, "Application protocol ID"); Args.notNull(upgradeHandler, "Protocol upgrade handler"); protocolUpgradeHandlerMap.put(TextUtils.toLowerCase(protocolId), upgradeHandler); } @Override public String toString() { final IOSession currentSession = currentSessionRef.get(); if (currentSession != null) { return currentSession.toString(); } else { return ioSession.toString(); } } } httpcore5/src/main/java/org/apache/hc/core5/reactor/ChannelEntry.java0100664 0000000 0000000 00000003257 14245617503 024511 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.nio.channels.SocketChannel; final class ChannelEntry { final SocketChannel channel; final Object attachment; public ChannelEntry(final SocketChannel channel, final Object attachment) { super(); this.channel = channel; this.attachment = attachment; } @Override public String toString() { return "[" + "channel=" + channel + ", attachment=" + attachment + ']'; } } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOReactorConfig.java0100664 0000000 0000000 00000040701 14435411677 025074 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.net.SocketAddress; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * I/O reactor configuration parameters. * * @since 4.2 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class IOReactorConfig { public static final IOReactorConfig DEFAULT = new Builder().build(); private final TimeValue selectInterval; private final int ioThreadCount; private final Timeout soTimeout; private final boolean soReuseAddress; private final TimeValue soLinger; private final boolean soKeepAlive; private final boolean tcpNoDelay; private final int trafficClass; private final int sndBufSize; private final int rcvBufSize; private final int backlogSize; private final SocketAddress socksProxyAddress; private final String socksProxyUsername; private final String socksProxyPassword; IOReactorConfig( final TimeValue selectInterval, final int ioThreadCount, final Timeout soTimeout, final boolean soReuseAddress, final TimeValue soLinger, final boolean soKeepAlive, final boolean tcpNoDelay, final int trafficClass, final int sndBufSize, final int rcvBufSize, final int backlogSize, final SocketAddress socksProxyAddress, final String socksProxyUsername, final String socksProxyPassword) { super(); this.selectInterval = selectInterval; this.ioThreadCount = ioThreadCount; this.soTimeout = soTimeout; this.soReuseAddress = soReuseAddress; this.soLinger = soLinger; this.soKeepAlive = soKeepAlive; this.tcpNoDelay = tcpNoDelay; this.trafficClass = trafficClass; this.sndBufSize = sndBufSize; this.rcvBufSize = rcvBufSize; this.backlogSize = backlogSize; this.socksProxyAddress = socksProxyAddress; this.socksProxyUsername = socksProxyUsername; this.socksProxyPassword = socksProxyPassword; } /** * @see Builder#setSelectInterval(TimeValue) */ public TimeValue getSelectInterval() { return this.selectInterval; } /** * @see Builder#setIoThreadCount(int) */ public int getIoThreadCount() { return this.ioThreadCount; } /** * @see Builder#setSoTimeout(Timeout) */ public Timeout getSoTimeout() { return soTimeout; } /** * @see Builder#setSoReuseAddress(boolean) */ public boolean isSoReuseAddress() { return soReuseAddress; } /** * @see Builder#setSoLinger(TimeValue) */ public TimeValue getSoLinger() { return soLinger; } /** * @see Builder#setSoKeepAlive(boolean) * @since 5.2 */ public boolean isSoKeepAlive() { return this.soKeepAlive; } /** * @see Builder#setSoKeepAlive(boolean) * @deprecated use {@link #isSoKeepAlive()}. */ @Deprecated public boolean isSoKeepalive() { return this.soKeepAlive; } /** * @see Builder#setTcpNoDelay(boolean) */ public boolean isTcpNoDelay() { return tcpNoDelay; } /** * @see Builder#setTrafficClass(int) * * @since 5.1 */ public int getTrafficClass() { return trafficClass; } /** * @see Builder#setSndBufSize(int) */ public int getSndBufSize() { return sndBufSize; } /** * @see Builder#setRcvBufSize(int) */ public int getRcvBufSize() { return rcvBufSize; } /** * @see Builder#setBacklogSize(int) */ public int getBacklogSize() { return backlogSize; } /** * @see Builder#setSocksProxyAddress(SocketAddress) */ public SocketAddress getSocksProxyAddress() { return this.socksProxyAddress; } /** * @see Builder#setSocksProxyUsername(String) */ public String getSocksProxyUsername() { return this.socksProxyUsername; } /** * @see Builder#setSocksProxyAddress(SocketAddress) */ public String getSocksProxyPassword() { return this.socksProxyPassword; } public static Builder custom() { return new Builder(); } public static Builder copy(final IOReactorConfig config) { Args.notNull(config, "I/O reactor config"); return new Builder() .setSelectInterval(config.getSelectInterval()) .setIoThreadCount(config.getIoThreadCount()) .setSoTimeout(config.getSoTimeout()) .setSoReuseAddress(config.isSoReuseAddress()) .setSoLinger(config.getSoLinger()) .setSoKeepAlive(config.isSoKeepAlive()) .setTcpNoDelay(config.isTcpNoDelay()) .setSndBufSize(config.getSndBufSize()) .setRcvBufSize(config.getRcvBufSize()) .setBacklogSize(config.getBacklogSize()) .setSocksProxyAddress(config.getSocksProxyAddress()) .setSocksProxyUsername(config.getSocksProxyUsername()) .setSocksProxyPassword(config.getSocksProxyPassword()); } public static class Builder { private static int defaultMaxIOThreadCount = -1; /** * Gets the default value for {@code ioThreadCount}. Returns * {@link Runtime#availableProcessors()} if * {@link #setDefaultMaxIOThreadCount(int)} was called with a value less <= 0. * * @return the default value for ioThreadCount. * @since 4.4.10 */ public static int getDefaultMaxIOThreadCount() { return defaultMaxIOThreadCount > 0 ? defaultMaxIOThreadCount : Runtime.getRuntime().availableProcessors(); } /** * Sets the default value for {@code ioThreadCount}. Use a value <= 0 to * cause {@link #getDefaultMaxIOThreadCount()} to return * {@link Runtime#availableProcessors()}. * * @param defaultMaxIOThreadCount * the default value for ioThreadCount. * @since 4.4.10 */ public static void setDefaultMaxIOThreadCount(final int defaultMaxIOThreadCount) { Builder.defaultMaxIOThreadCount = defaultMaxIOThreadCount; } private TimeValue selectInterval; private int ioThreadCount; private Timeout soTimeout; private boolean soReuseAddress; private TimeValue soLinger; private boolean soKeepAlive; private boolean tcpNoDelay; private int trafficClass; private int sndBufSize; private int rcvBufSize; private int backlogSize; private SocketAddress socksProxyAddress; private String socksProxyUsername; private String socksProxyPassword; Builder() { this.selectInterval = TimeValue.ofSeconds(1); this.ioThreadCount = Builder.getDefaultMaxIOThreadCount(); this.soTimeout = Timeout.ZERO_MILLISECONDS; this.soReuseAddress = false; this.soLinger = TimeValue.NEG_ONE_SECOND; this.soKeepAlive = false; this.tcpNoDelay = true; this.trafficClass = 0; this.sndBufSize = 0; this.rcvBufSize = 0; this.backlogSize = 0; this.socksProxyAddress = null; this.socksProxyUsername = null; this.socksProxyPassword = null; } /** * Determines time interval at which the I/O reactor wakes up to check for timed out sessions * and session requests. *

* Default: {@code 1000} milliseconds. *

*/ public Builder setSelectInterval(final TimeValue selectInterval) { this.selectInterval = selectInterval; return this; } /** * Determines the number of I/O dispatch threads to be used by the I/O reactor. *

* Default: {@code 2} *

*/ public Builder setIoThreadCount(final int ioThreadCount) { this.ioThreadCount = ioThreadCount; return this; } /** * Determines the default socket timeout value for non-blocking I/O operations. *

* Default: {@code 0} (no timeout) *

* * @see java.net.SocketOptions#SO_TIMEOUT */ public Builder setSoTimeout(final int soTimeout, final TimeUnit timeUnit) { this.soTimeout = Timeout.of(soTimeout, timeUnit); return this; } /** * Determines the default socket timeout value for non-blocking I/O operations. *

* Default: {@code 0} (no timeout) *

* * @see java.net.SocketOptions#SO_TIMEOUT */ public Builder setSoTimeout(final Timeout soTimeout) { this.soTimeout = soTimeout; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#SO_REUSEADDR} parameter * for newly created sockets. *

* Default: {@code false} *

* * @see java.net.SocketOptions#SO_REUSEADDR */ public Builder setSoReuseAddress(final boolean soReuseAddress) { this.soReuseAddress = soReuseAddress; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#SO_LINGER} parameter * for newly created sockets. *

* Default: {@code -1} *

* * @see java.net.SocketOptions#SO_LINGER */ public Builder setSoLinger(final int soLinger, final TimeUnit timeUnit) { this.soLinger = TimeValue.of(soLinger, timeUnit); return this; } /** * Determines the default value of the {@link java.net.SocketOptions#SO_LINGER} parameter * for newly created sockets. *

* Default: {@code -1} *

* * @see java.net.SocketOptions#SO_LINGER */ public Builder setSoLinger(final TimeValue soLinger) { this.soLinger = soLinger; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#SO_KEEPALIVE} parameter * for newly created sockets. *

* Default: {@code -1} *

* * @see java.net.SocketOptions#SO_KEEPALIVE */ public Builder setSoKeepAlive(final boolean soKeepAlive) { this.soKeepAlive = soKeepAlive; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#TCP_NODELAY} parameter * for newly created sockets. *

* Default: {@code false} *

* * @see java.net.SocketOptions#TCP_NODELAY */ public Builder setTcpNoDelay(final boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#IP_TOS} parameter * for newly created sockets. *

* Default: {@code 0} *

* * @see java.net.SocketOptions#IP_TOS * * @since 5.1 */ public Builder setTrafficClass(final int trafficClass) { this.trafficClass = trafficClass; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#SO_SNDBUF} parameter * for newly created sockets. *

* Default: {@code 0} (system default) *

* * @see java.net.SocketOptions#SO_SNDBUF */ public Builder setSndBufSize(final int sndBufSize) { this.sndBufSize = sndBufSize; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#SO_RCVBUF} parameter * for newly created sockets. *

* Default: {@code 0} (system default) *

* * @see java.net.SocketOptions#SO_RCVBUF */ public Builder setRcvBufSize(final int rcvBufSize) { this.rcvBufSize = rcvBufSize; return this; } /** * Determines the default backlog size value for server sockets binds. *

* Default: {@code 0} (system default) *

* * @since 4.4 */ public Builder setBacklogSize(final int backlogSize) { this.backlogSize = backlogSize; return this; } /** * The address of the SOCKS proxy to use. */ public Builder setSocksProxyAddress(final SocketAddress socksProxyAddress) { this.socksProxyAddress = socksProxyAddress; return this; } /** * The username to provide to the SOCKS proxy for username/password authentication. */ public Builder setSocksProxyUsername(final String socksProxyUsername) { this.socksProxyUsername = socksProxyUsername; return this; } /** * The password to provide to the SOCKS proxy for username/password authentication. */ public Builder setSocksProxyPassword(final String socksProxyPassword) { this.socksProxyPassword = socksProxyPassword; return this; } public IOReactorConfig build() { return new IOReactorConfig( selectInterval != null ? selectInterval : TimeValue.ofSeconds(1), ioThreadCount, Timeout.defaultsToDisabled(soTimeout), soReuseAddress, TimeValue.defaultsToNegativeOneMillisecond(soLinger), soKeepAlive, tcpNoDelay, trafficClass, sndBufSize, rcvBufSize, backlogSize, socksProxyAddress, socksProxyUsername, socksProxyPassword); } } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("[selectInterval=").append(this.selectInterval) .append(", ioThreadCount=").append(this.ioThreadCount) .append(", soTimeout=").append(this.soTimeout) .append(", soReuseAddress=").append(this.soReuseAddress) .append(", soLinger=").append(this.soLinger) .append(", soKeepAlive=").append(this.soKeepAlive) .append(", tcpNoDelay=").append(this.tcpNoDelay) .append(", trafficClass=").append(this.trafficClass) .append(", sndBufSize=").append(this.sndBufSize) .append(", rcvBufSize=").append(this.rcvBufSize) .append(", backlogSize=").append(this.backlogSize) .append(", socksProxyAddress=").append(this.socksProxyAddress) .append("]"); return builder.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/reactor/SocksProxyProtocolHandler.java0100664 0000000 0000000 00000036345 14435411677 027274 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.channels.SelectionKey; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.nio.command.CommandSupport; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.SocketTimeoutExceptionFactory; import org.apache.hc.core5.net.InetAddressUtils; import org.apache.hc.core5.util.Timeout; /** * Implements the client side of SOCKS protocol version 5 as per https://tools.ietf.org/html/rfc1928. Supports SOCKS username/password * authentication as per https://tools.ietf.org/html/rfc1929. */ final class SocksProxyProtocolHandler implements IOEventHandler { private static final int MAX_DNS_NAME_LENGTH = 255; private static final int MAX_COMMAND_CONNECT_LENGTH = 6 + MAX_DNS_NAME_LENGTH + 1; private static final byte CLIENT_VERSION = 5; private static final byte NO_AUTHENTICATION_REQUIRED = 0; private static final byte USERNAME_PASSWORD = 2; private static final byte USERNAME_PASSWORD_VERSION = 1; private static final byte SUCCESS = 0; private static final byte COMMAND_CONNECT = 1; private static final byte ATYP_DOMAINNAME = 3; private enum State { SEND_AUTH, RECEIVE_AUTH_METHOD, SEND_USERNAME_PASSWORD, RECEIVE_AUTH, SEND_CONNECT, RECEIVE_RESPONSE_CODE, RECEIVE_ADDRESS_TYPE, RECEIVE_ADDRESS, COMPLETE } private final InternalDataChannel dataChannel; private final IOSessionRequest sessionRequest; private final IOEventHandlerFactory eventHandlerFactory; private final IOReactorConfig reactorConfig; private ByteBuffer buffer = ByteBuffer.allocate(512); private State state = State.SEND_AUTH; SocksProxyProtocolHandler(final InternalDataChannel dataChannel, final IOSessionRequest sessionRequest, final IOEventHandlerFactory eventHandlerFactory, final IOReactorConfig reactorConfig) { this.dataChannel = dataChannel; this.sessionRequest = sessionRequest; this.eventHandlerFactory = eventHandlerFactory; this.reactorConfig = reactorConfig; } @Override public void connected(final IOSession session) throws IOException { this.buffer.put(CLIENT_VERSION); this.buffer.put((byte) 1); this.buffer.put(NO_AUTHENTICATION_REQUIRED); this.buffer.flip(); session.setEventMask(SelectionKey.OP_WRITE); } @Override public void outputReady(final IOSession session) throws IOException { switch (this.state) { case SEND_AUTH: if (writeAndPrepareRead(session, 2)) { session.setEventMask(SelectionKey.OP_READ); this.state = State.RECEIVE_AUTH_METHOD; } break; case SEND_USERNAME_PASSWORD: if (writeAndPrepareRead(session, 2)) { session.setEventMask(SelectionKey.OP_READ); this.state = State.RECEIVE_AUTH; } break; case SEND_CONNECT: if (writeAndPrepareRead(session, 2)) { session.setEventMask(SelectionKey.OP_READ); this.state = State.RECEIVE_RESPONSE_CODE; } break; case RECEIVE_AUTH_METHOD: case RECEIVE_AUTH: case RECEIVE_ADDRESS: case RECEIVE_ADDRESS_TYPE: case RECEIVE_RESPONSE_CODE: session.setEventMask(SelectionKey.OP_READ); break; case COMPLETE: break; } } private byte[] cred(final String cred) throws IOException { if (cred == null) { return new byte[] {}; } final byte[] bytes = cred.getBytes(StandardCharsets.ISO_8859_1); if (bytes.length >= 255) { throw new IOException("SOCKS username / password are too long"); } return bytes; } @Override public void inputReady(final IOSession session, final ByteBuffer src) throws IOException { if (src != null) { try { this.buffer.put(src); } catch (final BufferOverflowException ex) { throw new IOException("Unexpected input data"); } } switch (this.state) { case RECEIVE_AUTH_METHOD: if (fillBuffer(session)) { this.buffer.flip(); final byte serverVersion = this.buffer.get(); final byte serverMethod = this.buffer.get(); if (serverVersion != CLIENT_VERSION) { throw new IOException("SOCKS server returned unsupported version: " + serverVersion); } if (serverMethod == USERNAME_PASSWORD) { this.buffer.clear(); final byte[] username = cred(reactorConfig.getSocksProxyUsername()); final byte[] password = cred(reactorConfig.getSocksProxyPassword()); setBufferLimit(username.length + password.length + 3); this.buffer.put(USERNAME_PASSWORD_VERSION); this.buffer.put((byte) username.length); this.buffer.put(username); this.buffer.put((byte) password.length); this.buffer.put(password); session.setEventMask(SelectionKey.OP_WRITE); this.state = State.SEND_USERNAME_PASSWORD; } else if (serverMethod == NO_AUTHENTICATION_REQUIRED) { prepareConnectCommand(); session.setEventMask(SelectionKey.OP_WRITE); this.state = State.SEND_CONNECT; } else { throw new IOException("SOCKS server return unsupported authentication method: " + serverMethod); } } break; case RECEIVE_AUTH: if (fillBuffer(session)) { this.buffer.flip(); this.buffer.get(); // skip server auth version final byte status = this.buffer.get(); if (status != SUCCESS) { throw new IOException("Authentication failed for external SOCKS proxy"); } prepareConnectCommand(); session.setEventMask(SelectionKey.OP_WRITE); this.state = State.SEND_CONNECT; } break; case RECEIVE_RESPONSE_CODE: if (fillBuffer(session)) { this.buffer.flip(); final byte serverVersion = this.buffer.get(); final byte responseCode = this.buffer.get(); if (serverVersion != CLIENT_VERSION) { throw new IOException("SOCKS server returned unsupported version: " + serverVersion); } switch (responseCode) { case SUCCESS: break; case 1: throw new IOException("SOCKS: General SOCKS server failure"); case 2: throw new IOException("SOCKS5: Connection not allowed by ruleset"); case 3: throw new IOException("SOCKS5: Network unreachable"); case 4: throw new IOException("SOCKS5: Host unreachable"); case 5: throw new IOException("SOCKS5: Connection refused"); case 6: throw new IOException("SOCKS5: TTL expired"); case 7: throw new IOException("SOCKS5: Command not supported"); case 8: throw new IOException("SOCKS5: Address type not supported"); default: throw new IOException("SOCKS5: Unexpected SOCKS response code " + responseCode); } this.buffer.compact(); this.buffer.limit(3); this.state = State.RECEIVE_ADDRESS_TYPE; // deliberate fall-through } else { break; } case RECEIVE_ADDRESS_TYPE: if (fillBuffer(session)) { this.buffer.flip(); this.buffer.get(); // reserved byte that has no purpose final byte aType = this.buffer.get(); final int addressSize; if (aType == InetAddressUtils.IPV4) { addressSize = 4; } else if (aType == InetAddressUtils.IPV6) { addressSize = 16; } else if (aType == ATYP_DOMAINNAME) { // mask with 0xFF to convert to unsigned byte value addressSize = this.buffer.get() & 0xFF; } else { throw new IOException("SOCKS server returned unsupported address type: " + aType); } final int remainingResponseSize = addressSize + 2; this.buffer.compact(); // make sure we only read what we need to, don't read too much this.buffer.limit(remainingResponseSize); this.state = State.RECEIVE_ADDRESS; // deliberate fall-through } else { break; } case RECEIVE_ADDRESS: if (fillBuffer(session)) { this.buffer.clear(); this.state = State.COMPLETE; final IOEventHandler newHandler = this.eventHandlerFactory.createHandler(dataChannel, sessionRequest.attachment); dataChannel.upgrade(newHandler); sessionRequest.completed(dataChannel); dataChannel.handleIOEvent(SelectionKey.OP_CONNECT); } break; case SEND_AUTH: case SEND_USERNAME_PASSWORD: case SEND_CONNECT: session.setEventMask(SelectionKey.OP_WRITE); break; case COMPLETE: break; } } private void prepareConnectCommand() throws IOException { this.buffer.clear(); setBufferLimit(MAX_COMMAND_CONNECT_LENGTH); this.buffer.put(CLIENT_VERSION); this.buffer.put(COMMAND_CONNECT); this.buffer.put((byte) 0); // reserved if (!(sessionRequest.remoteAddress instanceof InetSocketAddress)) { throw new IOException("Unsupported address class: " + sessionRequest.remoteAddress.getClass()); } final InetSocketAddress targetAddress = ((InetSocketAddress) sessionRequest.remoteAddress); if (targetAddress.isUnresolved()) { this.buffer.put(ATYP_DOMAINNAME); final String hostName = targetAddress.getHostName(); final byte[] hostnameBytes = hostName.getBytes(StandardCharsets.US_ASCII); if (hostnameBytes.length > MAX_DNS_NAME_LENGTH) { throw new IOException("Host name exceeds " + MAX_DNS_NAME_LENGTH + " bytes"); } this.buffer.put((byte) hostnameBytes.length); this.buffer.put(hostnameBytes); } else { final InetAddress address = targetAddress.getAddress(); if (address instanceof Inet4Address) { this.buffer.put(InetAddressUtils.IPV4); } else if (address instanceof Inet6Address) { this.buffer.put(InetAddressUtils.IPV6); } else { throw new IOException("Unsupported remote address class: " + address.getClass().getName()); } this.buffer.put(address.getAddress()); } final int port = targetAddress.getPort(); this.buffer.putShort((short) port); this.buffer.flip(); } private void setBufferLimit(final int newLimit) { if (this.buffer.capacity() < newLimit) { final ByteBuffer newBuffer = ByteBuffer.allocate(newLimit); this.buffer.flip(); newBuffer.put(this.buffer); this.buffer = newBuffer; } else { this.buffer.limit(newLimit); } } private boolean writeAndPrepareRead(final ByteChannel channel, final int readSize) throws IOException { if (writeBuffer(channel)) { this.buffer.clear(); setBufferLimit(readSize); return true; } return false; } private boolean writeBuffer(final ByteChannel channel) throws IOException { if (this.buffer.hasRemaining()) { channel.write(this.buffer); } return !this.buffer.hasRemaining(); } private boolean fillBuffer(final ByteChannel channel) throws IOException { if (this.buffer.hasRemaining()) { channel.read(this.buffer); } return !this.buffer.hasRemaining(); } @Override public void timeout(final IOSession session, final Timeout timeout) throws IOException { exception(session, SocketTimeoutExceptionFactory.create(timeout)); } @Override public void exception(final IOSession session, final Exception cause) { try { sessionRequest.failed(cause); } finally { session.close(CloseMode.IMMEDIATE); CommandSupport.failCommands(session, cause); } } @Override public void disconnected(final IOSession session) { sessionRequest.cancel(); CommandSupport.cancelCommands(session); } } httpcore5/src/main/java/org/apache/hc/core5/reactor/ListenerEndpoint.java0100664 0000000 0000000 00000003572 14245617503 025405 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.net.SocketAddress; import org.apache.hc.core5.io.ModalCloseable; /** * ListenerEndpoint interface represents an endpoint used by an I/O reactor * to listen for incoming connection from remote clients. * * @since 4.0 */ public interface ListenerEndpoint extends ModalCloseable { /** * Returns the socket address of this endpoint. * * @return socket address. */ SocketAddress getAddress(); /** * Determines if this endpoint has been closed and is no longer listens * for incoming connections. * * @return {@code true} if the endpoint has been closed, * {@code false} otherwise. */ boolean isClosed(); } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOEventHandler.java0100664 0000000 0000000 00000005607 14245617503 024727 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.util.Timeout; /** * IOEventHandler interface is used by I/O reactors to handle I/O events for individual * I/O sessions. All methods of this interface are executed on a single dispatch thread * of the I/O reactor. Therefore, it is important that event processing does not not block * the I/O dispatch thread for too long, thus making the I/O reactor unable to react to * other events. * * @since 5.0 */ @Internal public interface IOEventHandler { /** * Triggered after the given session has been just created. * * @param session the I/O session. */ void connected(IOSession session) throws IOException; /** * Triggered when the given session has input pending. * * @param session the I/O session. */ void inputReady(IOSession session, ByteBuffer src) throws IOException; /** * Triggered when the given session is ready for output. * * @param session the I/O session. */ void outputReady(IOSession session) throws IOException; /** * Triggered when the given session has timed out. * * @param session the I/O session. * @param timeout the timeout. */ void timeout(IOSession session, Timeout timeout) throws IOException; /** * Triggered when the given session throws a exception. * * @param session the I/O session. */ void exception(IOSession session, Exception cause); /** * Triggered when the given session has been terminated. * * @param session the I/O session. */ void disconnected(IOSession session); } httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/0040775 0000000 0000000 00000000000 14435411752 022050 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/package-info.java0100664 0000000 0000000 00000002367 14245617503 025245 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * TLS/SSL support for I/O reactors. */ package org.apache.hc.core5.reactor.ssl; httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLManagedBuffer.java0100664 0000000 0000000 00000007711 14245617503 025767 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor.ssl; import java.nio.ByteBuffer; import org.apache.hc.core5.util.Args; abstract class SSLManagedBuffer { /** * Allocates the resources required for this buffer, or returns the resources already allocated for this buffer. * Unless {@link #release() } is called, multiple invocations to this method must return the same * {@link java.nio.ByteBuffer}. * @return buffer */ abstract ByteBuffer acquire(); /** * Releases the resources for this buffer. If the buffer has already been released, this method does nothing. */ abstract void release(); /** * Tests to see if this buffer has been acquired. * @return {@code true} if the buffer is acquired, otherwise {@code false} */ abstract boolean isAcquired(); /** * Tests to make sure that the buffer has been acquired and the underlying buffer has a position larger than * {@code 0}. Essentially the same as {@code isAquired() && acquire().position > 0}. * @return {@code true} if the buffer has been acquired and the underlying buffer's position is {@code > 0}, * otherwise {@code false} */ abstract boolean hasData(); static SSLManagedBuffer create(final SSLBufferMode mode, final int size) { return mode == SSLBufferMode.DYNAMIC ? new DynamicBuffer(size) : new StaticBuffer(size); } static final class StaticBuffer extends SSLManagedBuffer { private final ByteBuffer buffer; public StaticBuffer(final int size) { Args.positive(size, "size"); buffer = ByteBuffer.allocate(size); } @Override public ByteBuffer acquire() { return buffer; } @Override public void release() { // do nothing } @Override public boolean isAcquired() { return true; } @Override public boolean hasData() { return buffer.position() > 0; } } static final class DynamicBuffer extends SSLManagedBuffer { private ByteBuffer wrapped; private final int length; public DynamicBuffer(final int size) { Args.positive(size, "size"); this.length = size; } @Override public ByteBuffer acquire() { if (wrapped != null) { return wrapped; } wrapped = ByteBuffer.allocate(length); return wrapped; } @Override public void release() { wrapped = null; } @Override public boolean isAcquired() { return wrapped != null; } @Override public boolean hasData() { return wrapped != null && wrapped.position() > 0; } } } httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java0100664 0000000 0000000 00000102647 14435411752 025157 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor.ssl; import java.io.IOException; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.SocketTimeoutExceptionFactory; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.EventMask; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.Timeout; /** * {@code SSLIOSession} is a decorator class intended to transparently extend * an {@link IOSession} with transport layer security capabilities based on * the SSL/TLS protocol. * * @since 4.2 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) @Internal public class SSLIOSession implements IOSession { enum TLSHandShakeState { READY, INITIALIZED, HANDSHAKING, COMPLETE } private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); private final NamedEndpoint targetEndpoint; private final IOSession session; private final SSLEngine sslEngine; private final SSLManagedBuffer inEncrypted; private final SSLManagedBuffer outEncrypted; private final SSLManagedBuffer inPlain; private final SSLSessionInitializer initializer; private final SSLSessionVerifier verifier; private final Callback sessionStartCallback; private final Callback sessionEndCallback; private final AtomicReference> handshakeCallbackRef; private final Timeout handshakeTimeout; private final SSLMode sslMode; private final AtomicInteger outboundClosedCount; private final AtomicReference handshakeStateRef; private final IOEventHandler internalEventHandler; private int appEventMask; private volatile boolean endOfStream; private volatile Status status = Status.ACTIVE; private volatile Timeout socketTimeout; private volatile TlsDetails tlsDetails; /** * Creates new instance of {@code SSLIOSession} class. * * @param session I/O session to be decorated with the TLS/SSL capabilities. * @param sslMode SSL mode (client or server) * @param targetEndpoint target endpoint (applicable in client mode only). May be {@code null}. * @param sslContext SSL context to use for this I/O session. * @param sslBufferMode buffer management mode * @param initializer optional SSL session initializer. May be {@code null}. * @param verifier optional SSL session verifier. May be {@code null}. * @param connectTimeout timeout to apply for the TLS/SSL handshake. May be {@code null}. * * @since 5.0 */ public SSLIOSession( final NamedEndpoint targetEndpoint, final IOSession session, final SSLMode sslMode, final SSLContext sslContext, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier, final Callback sessionStartCallback, final Callback sessionEndCallback, final Timeout connectTimeout) { this(targetEndpoint, session, sslMode, sslContext, sslBufferMode, initializer, verifier, connectTimeout, sessionStartCallback, sessionEndCallback, null); } /** * Creates new instance of {@code SSLIOSession} class. * * @param session I/O session to be decorated with the TLS/SSL capabilities. * @param sslMode SSL mode (client or server) * @param targetEndpoint target endpoint (applicable in client mode only). May be {@code null}. * @param sslContext SSL context to use for this I/O session. * @param sslBufferMode buffer management mode * @param initializer optional SSL session initializer. May be {@code null}. * @param verifier optional SSL session verifier. May be {@code null}. * @param handshakeTimeout timeout to apply for the TLS/SSL handshake. May be {@code null}. * @param resultCallback result callback. May be {@code null}. * * @since 5.2 */ public SSLIOSession( final NamedEndpoint targetEndpoint, final IOSession session, final SSLMode sslMode, final SSLContext sslContext, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier, final Timeout handshakeTimeout, final Callback sessionStartCallback, final Callback sessionEndCallback, final FutureCallback resultCallback) { super(); Args.notNull(session, "IO session"); Args.notNull(sslContext, "SSL context"); this.targetEndpoint = targetEndpoint; this.session = session; this.sslMode = sslMode; this.initializer = initializer; this.verifier = verifier; this.sessionStartCallback = sessionStartCallback; this.sessionEndCallback = sessionEndCallback; this.handshakeCallbackRef = new AtomicReference<>(resultCallback); this.appEventMask = session.getEventMask(); if (this.sslMode == SSLMode.CLIENT && targetEndpoint != null) { this.sslEngine = sslContext.createSSLEngine(targetEndpoint.getHostName(), targetEndpoint.getPort()); } else { this.sslEngine = sslContext.createSSLEngine(); } final SSLSession sslSession = this.sslEngine.getSession(); // Allocate buffers for network (encrypted) data final int netBufferSize = sslSession.getPacketBufferSize(); this.inEncrypted = SSLManagedBuffer.create(sslBufferMode, netBufferSize); this.outEncrypted = SSLManagedBuffer.create(sslBufferMode, netBufferSize); // Allocate buffers for application (unencrypted) data final int appBufferSize = sslSession.getApplicationBufferSize(); this.inPlain = SSLManagedBuffer.create(sslBufferMode, appBufferSize); this.outboundClosedCount = new AtomicInteger(0); this.handshakeStateRef = new AtomicReference<>(TLSHandShakeState.READY); this.handshakeTimeout = handshakeTimeout; this.internalEventHandler = new IOEventHandler() { @Override public void connected(final IOSession protocolSession) throws IOException { beginHandshake(protocolSession); } @Override public void inputReady(final IOSession protocolSession, final ByteBuffer src) throws IOException { receiveEncryptedData(); doHandshake(protocolSession); decryptData(protocolSession); updateEventMask(); } @Override public void outputReady(final IOSession protocolSession) throws IOException { encryptData(protocolSession); sendEncryptedData(); doHandshake(protocolSession); updateEventMask(); } @Override public void timeout(final IOSession protocolSession, final Timeout timeout) throws IOException { if (sslEngine.isInboundDone() && !sslEngine.isInboundDone()) { // The session failed to terminate cleanly close(CloseMode.IMMEDIATE); } if (handshakeStateRef.get() != TLSHandShakeState.COMPLETE) { exception(protocolSession, SocketTimeoutExceptionFactory.create(handshakeTimeout)); } else { ensureHandler().timeout(protocolSession, timeout); } } @Override public void exception(final IOSession protocolSession, final Exception cause) { final FutureCallback resultCallback = handshakeCallbackRef.getAndSet(null); if (resultCallback != null) { resultCallback.failed(cause); } final IOEventHandler handler = session.getHandler(); if (handshakeStateRef.get() != TLSHandShakeState.COMPLETE) { session.close(CloseMode.GRACEFUL); close(CloseMode.IMMEDIATE); } if (handler != null) { handler.exception(protocolSession, cause); } } @Override public void disconnected(final IOSession protocolSession) { final IOEventHandler handler = session.getHandler(); if (handler != null) { handler.disconnected(protocolSession); } } }; } private IOEventHandler ensureHandler() { final IOEventHandler handler = session.getHandler(); Asserts.notNull(handler, "IO event handler"); return handler; } @Override public IOEventHandler getHandler() { return internalEventHandler; } public void beginHandshake(final IOSession protocolSession) throws IOException { if (handshakeStateRef.compareAndSet(TLSHandShakeState.READY, TLSHandShakeState.INITIALIZED)) { initialize(protocolSession); } } private void initialize(final IOSession protocolSession) throws IOException { // Save the initial socketTimeout of the underlying IOSession, to be restored after the handshake is finished this.socketTimeout = this.session.getSocketTimeout(); if (handshakeTimeout != null) { this.session.setSocketTimeout(handshakeTimeout); } this.session.getLock().lock(); try { if (this.status.compareTo(Status.CLOSING) >= 0) { return; } switch (this.sslMode) { case CLIENT: this.sslEngine.setUseClientMode(true); break; case SERVER: this.sslEngine.setUseClientMode(false); break; } if (this.initializer != null) { this.initializer.initialize(this.targetEndpoint, this.sslEngine); } this.handshakeStateRef.set(TLSHandShakeState.HANDSHAKING); this.sslEngine.beginHandshake(); this.inEncrypted.release(); this.outEncrypted.release(); doHandshake(protocolSession); updateEventMask(); } finally { this.session.getLock().unlock(); } } // A works-around for exception handling craziness in Sun/Oracle's SSLEngine // implementation. // // sun.security.pkcs11.wrapper.PKCS11Exception is re-thrown as // plain RuntimeException in sun.security.ssl.Handshaker#checkThrown private SSLException convert(final RuntimeException ex) { Throwable cause = ex.getCause(); if (cause == null) { cause = ex; } return new SSLException(cause); } private SSLEngineResult doWrap(final ByteBuffer src, final ByteBuffer dst) throws SSLException { try { return this.sslEngine.wrap(src, dst); } catch (final RuntimeException ex) { throw convert(ex); } } private SSLEngineResult doUnwrap(final ByteBuffer src, final ByteBuffer dst) throws SSLException { try { return this.sslEngine.unwrap(src, dst); } catch (final RuntimeException ex) { throw convert(ex); } } private void doRunTask() { final Runnable r = this.sslEngine.getDelegatedTask(); if (r != null) { r.run(); } } private void doHandshake(final IOSession protocolSession) throws IOException { boolean handshaking = true; SSLEngineResult result = null; while (handshaking) { HandshakeStatus handshakeStatus = this.sslEngine.getHandshakeStatus(); // Work-around for what appears to be a bug in Conscrypt SSLEngine that does not // transition into the handshaking state upon #closeOutbound() call but still // has some handshake data stuck in its internal buffer. if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING && outboundClosedCount.get() > 0) { handshakeStatus = HandshakeStatus.NEED_WRAP; } switch (handshakeStatus) { case NEED_WRAP: // Generate outgoing handshake data this.session.getLock().lock(); try { // Acquire buffers final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire(); // Just wrap an empty buffer because there is no data to write. result = doWrap(EMPTY_BUFFER, outEncryptedBuf); if (result.getStatus() != SSLEngineResult.Status.OK || result.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { handshaking = false; } break; } finally { this.session.getLock().unlock(); } case NEED_UNWRAP: // Process incoming handshake data // Acquire buffers final ByteBuffer inEncryptedBuf = this.inEncrypted.acquire(); final ByteBuffer inPlainBuf = this.inPlain.acquire(); // Perform operations inEncryptedBuf.flip(); try { result = doUnwrap(inEncryptedBuf, inPlainBuf); } finally { inEncryptedBuf.compact(); } try { if (!inEncryptedBuf.hasRemaining() && result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) { throw new SSLException("Input buffer is full"); } } finally { // Release inEncrypted if empty if (inEncryptedBuf.position() == 0) { this.inEncrypted.release(); } } if (this.status.compareTo(Status.CLOSING) >= 0) { this.inPlain.release(); } if (result.getStatus() != SSLEngineResult.Status.OK) { handshaking = false; } break; case NEED_TASK: doRunTask(); break; case NOT_HANDSHAKING: handshaking = false; break; } } // The SSLEngine has just finished handshaking. This value is only generated by a call // to SSLEngine.wrap()/unwrap() when that call finishes a handshake. // It is never generated by SSLEngine.getHandshakeStatus(). if (result != null && result.getHandshakeStatus() == HandshakeStatus.FINISHED) { this.handshakeStateRef.set(TLSHandShakeState.COMPLETE); this.session.setSocketTimeout(this.socketTimeout); if (this.verifier != null) { this.tlsDetails = this.verifier.verify(this.targetEndpoint, this.sslEngine); } String applicationProtocol; if (this.tlsDetails == null) { final SSLSession sslSession = this.sslEngine.getSession(); try { applicationProtocol = this.sslEngine.getApplicationProtocol(); } catch (final UnsupportedOperationException e) { // If the underlying provider does not support the operation, the getApplicationProtocol() method throws an UnsupportedOperationException. // In this case, we fall back to "http/1.1" as the application protocol. // This is a workaround to allow older applications that do not support the getApplicationProtocol() method to continue working. // This workaround is temporary and is meant to maintain compatibility with older systems. applicationProtocol = "http/1.1"; } this.tlsDetails = new TlsDetails(sslSession, applicationProtocol); } ensureHandler().connected(protocolSession); if (this.sessionStartCallback != null) { this.sessionStartCallback.execute(this); } final FutureCallback resultCallback = handshakeCallbackRef.getAndSet(null); if (resultCallback != null) { resultCallback.completed(sslEngine.getSession()); } } } private void updateEventMask() { this.session.getLock().lock(); try { // Graceful session termination if (this.status == Status.ACTIVE && (this.endOfStream || this.sslEngine.isInboundDone())) { this.status = Status.CLOSING; final FutureCallback resultCallback = handshakeCallbackRef.getAndSet(null); if (resultCallback != null) { resultCallback.failed(new SSLHandshakeException("TLS handshake failed")); } } if (this.status == Status.CLOSING && !this.outEncrypted.hasData()) { this.sslEngine.closeOutbound(); this.outboundClosedCount.incrementAndGet(); } if (this.status == Status.CLOSING && this.sslEngine.isOutboundDone() && (this.endOfStream || this.sslEngine.isInboundDone())) { this.status = Status.CLOSED; } // Abnormal session termination if (this.status.compareTo(Status.CLOSING) <= 0 && this.endOfStream && this.sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) { this.status = Status.CLOSED; } if (this.status == Status.CLOSED) { this.session.close(); if (sessionEndCallback != null) { sessionEndCallback.execute(this); } return; } // Is there a task pending? if (this.sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { doRunTask(); } // Need to toggle the event mask for this channel? final int oldMask = this.session.getEventMask(); int newMask = oldMask; switch (this.sslEngine.getHandshakeStatus()) { case NEED_WRAP: newMask = EventMask.READ_WRITE; break; case NEED_UNWRAP: newMask = EventMask.READ; break; case NOT_HANDSHAKING: newMask = this.appEventMask; break; } if (this.endOfStream && !this.inPlain.hasData()) { newMask = newMask & ~EventMask.READ; } else if (this.status == Status.CLOSING) { newMask = newMask | EventMask.READ; } // Do we have encrypted data ready to be sent? if (this.outEncrypted.hasData()) { newMask = newMask | EventMask.WRITE; } else if (this.sslEngine.isOutboundDone()) { newMask = newMask & ~EventMask.WRITE; } // Update the mask if necessary if (oldMask != newMask) { this.session.setEventMask(newMask); } } finally { this.session.getLock().unlock(); } } private int sendEncryptedData() throws IOException { this.session.getLock().lock(); try { if (!this.outEncrypted.hasData()) { // If the buffer isn't acquired or is empty, call write() with an empty buffer. // This will ensure that tests performed by write() still take place without // having to acquire and release an empty buffer (e.g. connection closed, // interrupted thread, etc..) return this.session.write(EMPTY_BUFFER); } // Acquire buffer final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire(); // Clear output buffer if the session has been closed // in case there is still `close_notify` data stuck in it if (this.status == Status.CLOSED) { outEncryptedBuf.clear(); } // Perform operation int bytesWritten = 0; if (outEncryptedBuf.position() > 0) { outEncryptedBuf.flip(); try { bytesWritten = this.session.write(outEncryptedBuf); } finally { outEncryptedBuf.compact(); } } // Release if empty if (outEncryptedBuf.position() == 0) { this.outEncrypted.release(); } return bytesWritten; } finally { this.session.getLock().unlock(); } } private int receiveEncryptedData() throws IOException { if (this.endOfStream) { return -1; } // Acquire buffer final ByteBuffer inEncryptedBuf = this.inEncrypted.acquire(); // Perform operation final int bytesRead = this.session.read(inEncryptedBuf); // Release if empty if (inEncryptedBuf.position() == 0) { this.inEncrypted.release(); } if (bytesRead == -1) { this.endOfStream = true; } return bytesRead; } private void decryptData(final IOSession protocolSession) throws IOException { final HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus(); if ((handshakeStatus == HandshakeStatus.NOT_HANDSHAKING || handshakeStatus == HandshakeStatus.FINISHED) && inEncrypted.hasData()) { final ByteBuffer inEncryptedBuf = inEncrypted.acquire(); inEncryptedBuf.flip(); try { while (inEncryptedBuf.hasRemaining()) { final ByteBuffer inPlainBuf = inPlain.acquire(); try { final SSLEngineResult result = doUnwrap(inEncryptedBuf, inPlainBuf); if (!inEncryptedBuf.hasRemaining() && result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) { throw new SSLException("Unable to complete SSL handshake"); } if (sslEngine.isInboundDone()) { endOfStream = true; } if(inPlainBuf.position() > 0) { inPlainBuf.flip(); try { ensureHandler().inputReady(protocolSession, inPlainBuf.hasRemaining() ? inPlainBuf : null); } finally { inPlainBuf.clear(); } } if (result.getStatus() != SSLEngineResult.Status.OK) { if (result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW && endOfStream) { throw new SSLException("Unable to decrypt incoming data due to unexpected end of stream"); } break; } } finally { inPlain.release(); } } } finally { inEncryptedBuf.compact(); // Release inEncrypted if empty if (inEncryptedBuf.position() == 0) { inEncrypted.release(); } } } if (endOfStream && !inEncrypted.hasData()) { ensureHandler().inputReady(protocolSession, null); } } private void encryptData(final IOSession protocolSession) throws IOException { final boolean appReady; this.session.getLock().lock(); try { appReady = (this.appEventMask & SelectionKey.OP_WRITE) > 0 && this.status == Status.ACTIVE && this.sslEngine.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING; } finally { this.session.getLock().unlock(); } if (appReady) { ensureHandler().outputReady(protocolSession); } } @Override public int write(final ByteBuffer src) throws IOException { Args.notNull(src, "Byte buffer"); this.session.getLock().lock(); try { if (this.status != Status.ACTIVE) { throw new ClosedChannelException(); } if (this.handshakeStateRef.get() == TLSHandShakeState.READY) { return 0; } final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire(); final SSLEngineResult result = doWrap(src, outEncryptedBuf); return result.bytesConsumed(); } finally { this.session.getLock().unlock(); } } @Override public int read(final ByteBuffer dst) { return endOfStream ? -1 : 0; } @Override public String getId() { return session.getId(); } @Override public Lock getLock() { return this.session.getLock(); } @Override public void upgrade(final IOEventHandler handler) { this.session.upgrade(handler); } public TlsDetails getTlsDetails() { return tlsDetails; } @Override public boolean isOpen() { return this.status == Status.ACTIVE && this.session.isOpen(); } @Override public void close() { close(CloseMode.GRACEFUL); } @Override public void close(final CloseMode closeMode) { this.session.getLock().lock(); try { if (closeMode == CloseMode.GRACEFUL) { if (this.status.compareTo(Status.CLOSING) >= 0) { return; } this.status = Status.CLOSING; if (this.session.getSocketTimeout().isDisabled()) { this.session.setSocketTimeout(Timeout.ofMilliseconds(1000)); } try { // Catch all unchecked exceptions in case something goes wrong // in the JSSE provider. For instance // com.android.org.conscrypt.NativeCrypto#SSL_get_shutdown can // throw NPE at this point updateEventMask(); } catch (final CancelledKeyException ex) { this.session.close(CloseMode.GRACEFUL); } catch (final Exception ex) { this.session.close(CloseMode.IMMEDIATE); } } else { if (this.status == Status.CLOSED) { return; } this.inEncrypted.release(); this.outEncrypted.release(); this.inPlain.release(); this.status = Status.CLOSED; this.session.close(closeMode); } } finally { this.session.getLock().unlock(); } } @Override public Status getStatus() { return this.status; } @Override public void enqueue(final Command command, final Command.Priority priority) { this.session.getLock().lock(); try { this.session.enqueue(command, priority); setEvent(SelectionKey.OP_WRITE); } finally { this.session.getLock().unlock(); } } @Override public boolean hasCommands() { return this.session.hasCommands(); } @Override public Command poll() { return this.session.poll(); } @Override public ByteChannel channel() { return this.session.channel(); } @Override public SocketAddress getLocalAddress() { return this.session.getLocalAddress(); } @Override public SocketAddress getRemoteAddress() { return this.session.getRemoteAddress(); } @Override public int getEventMask() { this.session.getLock().lock(); try { return this.appEventMask; } finally { this.session.getLock().unlock(); } } @Override public void setEventMask(final int ops) { this.session.getLock().lock(); try { this.appEventMask = ops; updateEventMask(); } finally { this.session.getLock().unlock(); } } @Override public void setEvent(final int op) { this.session.getLock().lock(); try { this.appEventMask = this.appEventMask | op; updateEventMask(); } finally { this.session.getLock().unlock(); } } @Override public void clearEvent(final int op) { this.session.getLock().lock(); try { this.appEventMask = this.appEventMask & ~op; updateEventMask(); } finally { this.session.getLock().unlock(); } } @Override public Timeout getSocketTimeout() { return this.session.getSocketTimeout(); } @Override public void setSocketTimeout(final Timeout timeout) { this.socketTimeout = timeout; if (this.sslEngine.getHandshakeStatus() == HandshakeStatus.FINISHED) { this.session.setSocketTimeout(timeout); } } @Override public void updateReadTime() { this.session.updateReadTime(); } @Override public void updateWriteTime() { this.session.updateWriteTime(); } @Override public long getLastReadTime() { return this.session.getLastReadTime(); } @Override public long getLastWriteTime() { return this.session.getLastWriteTime(); } @Override public long getLastEventTime() { return this.session.getLastEventTime(); } private static void formatOps(final StringBuilder buffer, final int ops) { if ((ops & SelectionKey.OP_READ) > 0) { buffer.append('r'); } if ((ops & SelectionKey.OP_WRITE) > 0) { buffer.append('w'); } } @Override public String toString() { this.session.getLock().lock(); try { final StringBuilder buffer = new StringBuilder(); buffer.append(this.session); buffer.append("["); buffer.append(this.status); buffer.append("]["); formatOps(buffer, this.appEventMask); buffer.append("]["); buffer.append(this.sslEngine.getHandshakeStatus()); if (this.sslEngine.isInboundDone()) { buffer.append("][inbound done]["); } if (this.sslEngine.isOutboundDone()) { buffer.append("][outbound done]["); } if (this.endOfStream) { buffer.append("][EOF]["); } buffer.append("]["); buffer.append(!this.inEncrypted.hasData() ? 0 : inEncrypted.acquire().position()); buffer.append("]["); buffer.append(!this.inPlain.hasData() ? 0 : inPlain.acquire().position()); buffer.append("]["); buffer.append(!this.outEncrypted.hasData() ? 0 : outEncrypted.acquire().position()); buffer.append("]"); return buffer.toString(); } finally { this.session.getLock().unlock(); } } } httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLMode.java0100664 0000000 0000000 00000002422 14245617503 024157 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor.ssl; /** * @since 4.2 */ public enum SSLMode { CLIENT, SERVER } httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLSessionVerifier.java0100664 0000000 0000000 00000004216 14245617503 026415 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor.ssl; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import org.apache.hc.core5.net.NamedEndpoint; /** * Callback interface that can be used to customize TLS/SSL session verification. * * @since 4.2 */ public interface SSLSessionVerifier { /** * Triggered when the SSL connection has been established and initial SSL * handshake has been successfully completed. Custom handlers can use * this callback to verify properties of the {@link SSLEngine}. * For instance this would be the right place to enforce SSL cipher * strength, validate certificate chain and do hostname checks. * * @param endpoint the endpoint name for a client side session or {@code null} * for a server side session. * @param sslEngine SSL engine. * @throws SSLException if case of SSL protocol error. */ TlsDetails verify(NamedEndpoint endpoint, SSLEngine sslEngine) throws SSLException; } httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/TransportSecurityLayer.java0100664 0000000 0000000 00000007565 14403631147 027443 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor.ssl; import javax.net.ssl.SSLContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.util.Timeout; /** * TLS capable session layer interface. * * @since 5.0 */ public interface TransportSecurityLayer { /** * Starts TLS session over an existing network connection with the given SSL context. * {@link NamedEndpoint} details are applicable for client side connections and * are used for host name verification, when supported by the SSL engine. * * @param sslContext SSL context to be used for this session. * @param endpoint optional endpoint details for outgoing client side connections. * @param sslBufferMode SSL buffer management mode. * @param initializer SSL session initialization callback. * @param verifier SSL session verification callback. * @param handshakeTimeout the timeout to use while performing the TLS handshake; may be {@code null}. */ void startTls( SSLContext sslContext, NamedEndpoint endpoint, SSLBufferMode sslBufferMode, SSLSessionInitializer initializer, SSLSessionVerifier verifier, Timeout handshakeTimeout) throws UnsupportedOperationException; /** * Starts TLS session over an existing network connection with the given SSL context. * {@link NamedEndpoint} details are applicable for client side connections and * are used for host name verification, when supported by the SSL engine. * * @param sslContext SSL context to be used for this session. * @param endpoint optional endpoint details for outgoing client side connections. * @param sslBufferMode SSL buffer management mode. * @param initializer SSL session initialization callback. * @param verifier SSL session verification callback. * @param handshakeTimeout the timeout to use while performing the TLS handshake; may be {@code null}. * * @since 5.2 */ default void startTls( SSLContext sslContext, NamedEndpoint endpoint, SSLBufferMode sslBufferMode, SSLSessionInitializer initializer, SSLSessionVerifier verifier, Timeout handshakeTimeout, FutureCallback callback) throws UnsupportedOperationException { startTls(sslContext, endpoint, sslBufferMode, initializer, verifier, handshakeTimeout); if (callback != null) { callback.completed(null); } } /** * Returns details of a fully established TLS session. * * @return TLS session details. */ TlsDetails getTlsDetails(); } httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLSessionInitializer.java0100664 0000000 0000000 00000003620 14245617503 027123 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor.ssl; import javax.net.ssl.SSLEngine; import org.apache.hc.core5.net.NamedEndpoint; /** * Callback interface that can be used to customize TLS/SSL session initialization. * * @since 4.2 */ public interface SSLSessionInitializer { /** * Triggered when the SSL connection is being initialized. Custom handlers * can use this callback to customize properties of the {@link javax.net.ssl.SSLEngine} * used to establish the SSL session. * * @param endpoint the endpoint name for a client side session or {@code null} * for a server side session. * @param sslEngine the SSL engine. */ void initialize(NamedEndpoint endpoint, SSLEngine sslEngine); } httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLBufferMode.java0100664 0000000 0000000 00000002431 14245617503 025311 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor.ssl; /** * @since 5.0 */ public enum SSLBufferMode { STATIC, DYNAMIC } httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/TlsDetails.java0100664 0000000 0000000 00000003767 14245617503 024776 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor.ssl; import javax.net.ssl.SSLSession; /** * Represents details of a fully established TLS session. * * @since 5.0 */ public final class TlsDetails { private final SSLSession sslSession; private final String applicationProtocol; public TlsDetails(final SSLSession sslSession, final String applicationProtocol) { this.sslSession = sslSession; this.applicationProtocol = applicationProtocol; } public SSLSession getSSLSession() { return sslSession; } public String getApplicationProtocol() { return applicationProtocol; } @Override public String toString() { return "TlsDetails{" + "sslSession=" + sslSession + ", applicationProtocol='" + applicationProtocol + '\'' + '}'; } } httpcore5/src/main/java/org/apache/hc/core5/reactor/AbstractIOReactorBase.java0100664 0000000 0000000 00000004641 14245617503 026223 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.net.SocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; abstract class AbstractIOReactorBase implements ConnectionInitiator, IOReactorService { @Override public final Future connect( final NamedEndpoint remoteEndpoint, final SocketAddress remoteAddress, final SocketAddress localAddress, final Timeout timeout, final Object attachment, final FutureCallback callback) throws IOReactorShutdownException { Args.notNull(remoteEndpoint, "Remote endpoint"); if (getStatus().compareTo(IOReactorStatus.ACTIVE) > 0) { throw new IOReactorShutdownException("I/O reactor has been shut down"); } try { return getWorkerSelector().next().connect(remoteEndpoint, remoteAddress, localAddress, timeout, attachment, callback); } catch (final IOReactorShutdownException ex) { initiateShutdown(); throw ex; } } abstract IOWorkers.Selector getWorkerSelector(); } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOReactorWorker.java0100664 0000000 0000000 00000003500 14245617503 025127 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; final class IOReactorWorker implements Runnable { private final AbstractSingleCoreIOReactor ioReactor; private volatile Throwable throwable; public IOReactorWorker(final AbstractSingleCoreIOReactor ioReactor) { super(); this.ioReactor = ioReactor; } @Override public void run() { try { this.ioReactor.execute(); } catch (final Error ex) { this.throwable = ex; throw ex; } catch (final Exception ex) { this.throwable = ex; } } public Throwable getThrowable() { return this.throwable; } } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOReactorService.java0100664 0000000 0000000 00000002602 14245617503 025260 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; /** * {@link IOReactor} running as a service. * * @since 5.0 */ public interface IOReactorService extends IOReactor { /** * Starts I/O reactor. */ void start(); } httpcore5/src/main/java/org/apache/hc/core5/reactor/EndpointParameters.java0100664 0000000 0000000 00000005533 14245617503 025722 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.net.Ports; import org.apache.hc.core5.util.Args; /** * Endpoint initialization parameters * * @since 5.1 */ @Internal public final class EndpointParameters implements NamedEndpoint { private final String scheme; private final String hostName; private final int port; private final Object attachment; public EndpointParameters(final String scheme, final String hostName, final int port, final Object attachment) { this.scheme = Args.notBlank(scheme, "Protocol scheme"); this.hostName = Args.notBlank(hostName, "Endpoint name"); this.port = Ports.checkWithDefault(port); this.attachment = attachment; } public EndpointParameters(final HttpHost host, final Object attachment) { Args.notNull(host, "HTTP host"); this.scheme = host.getSchemeName(); this.hostName = host.getHostName(); this.port = host.getPort(); this.attachment = attachment; } public String getScheme() { return scheme; } @Override public String getHostName() { return hostName; } @Override public int getPort() { return port; } public Object getAttachment() { return attachment; } @Override public String toString() { return "EndpointParameters{" + "scheme='" + scheme + '\'' + ", name='" + hostName + '\'' + ", port=" + port + ", attachment=" + attachment + '}'; } } httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalChannel.java0100664 0000000 0000000 00000005351 14245617503 025161 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.nio.channels.CancelledKeyException; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Timeout; abstract class InternalChannel implements ModalCloseable { abstract void onIOEvent(final int ops) throws IOException; abstract void onTimeout(Timeout timeout) throws IOException; abstract void onException(final Exception cause); abstract Timeout getTimeout(); abstract long getLastEventTime(); final void handleIOEvent(final int ops) { try { onIOEvent(ops); } catch (final CancelledKeyException ex) { close(CloseMode.GRACEFUL); } catch (final Exception ex) { onException(ex); close(CloseMode.GRACEFUL); } } final boolean checkTimeout(final long currentTimeMillis) { final Timeout timeout = getTimeout(); if (!timeout.isDisabled()) { final long timeoutMillis = timeout.toMilliseconds(); final long deadlineMillis = getLastEventTime() + timeoutMillis; if (currentTimeMillis > deadlineMillis) { try { onTimeout(timeout); } catch (final CancelledKeyException ex) { close(CloseMode.GRACEFUL); } catch (final Exception ex) { onException(ex); close(CloseMode.GRACEFUL); } return false; } } return true; } } httpcore5/src/main/java/org/apache/hc/core5/reactor/DefaultConnectingIOReactor.java0100664 0000000 0000000 00000011754 14245617503 027264 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.util.concurrent.ThreadFactory; import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; /** * Multi-core I/O reactor that can act as {@link ConnectionInitiator} Internally * this I/O reactor distributes newly created I/O session equally across multiple * I/O worker threads for a more optimal resource utilization and a better * I/O performance. Usually it is recommended to have one worker I/O reactor * per physical CPU core. * * @since 4.0 */ public class DefaultConnectingIOReactor extends AbstractIOReactorBase { private final int workerCount; private final SingleCoreIOReactor[] workers; private final MultiCoreIOReactor ioReactor; private final IOWorkers.Selector workerSelector; private final static ThreadFactory THREAD_FACTORY = new DefaultThreadFactory("I/O client dispatch", true); public DefaultConnectingIOReactor( final IOEventHandlerFactory eventHandlerFactory, final IOReactorConfig ioReactorConfig, final ThreadFactory threadFactory, final Decorator ioSessionDecorator, final Callback exceptionCallback, final IOSessionListener sessionListener, final Callback sessionShutdownCallback) { Args.notNull(eventHandlerFactory, "Event handler factory"); this.workerCount = ioReactorConfig != null ? ioReactorConfig.getIoThreadCount() : IOReactorConfig.DEFAULT.getIoThreadCount(); this.workers = new SingleCoreIOReactor[workerCount]; final Thread[] threads = new Thread[workerCount]; for (int i = 0; i < this.workers.length; i++) { final SingleCoreIOReactor dispatcher = new SingleCoreIOReactor( exceptionCallback, eventHandlerFactory, ioReactorConfig != null ? ioReactorConfig : IOReactorConfig.DEFAULT, ioSessionDecorator, sessionListener, sessionShutdownCallback); this.workers[i] = dispatcher; threads[i] = (threadFactory != null ? threadFactory : THREAD_FACTORY).newThread(new IOReactorWorker(dispatcher)); } this.ioReactor = new MultiCoreIOReactor(this.workers, threads); this.workerSelector = IOWorkers.newSelector(workers); } public DefaultConnectingIOReactor( final IOEventHandlerFactory eventHandlerFactory, final IOReactorConfig config, final Callback sessionShutdownCallback) { this(eventHandlerFactory, config, null, null, null, null, sessionShutdownCallback); } /** * Creates an instance of DefaultConnectingIOReactor with default configuration. * * @since 5.0 */ public DefaultConnectingIOReactor(final IOEventHandlerFactory eventHandlerFactory) { this(eventHandlerFactory, null, null); } @Override public void start() { ioReactor.start(); } @Override public IOReactorStatus getStatus() { return ioReactor.getStatus(); } @Override IOWorkers.Selector getWorkerSelector() { return workerSelector; } @Override public void initiateShutdown() { ioReactor.initiateShutdown(); } @Override public void awaitShutdown(final TimeValue waitTime) throws InterruptedException { ioReactor.awaitShutdown(waitTime); } @Override public void close(final CloseMode closeMode) { ioReactor.close(closeMode); } @Override public void close() throws IOException { ioReactor.close(); } } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSessionListener.java0100664 0000000 0000000 00000003221 14245617503 025467 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import org.apache.hc.core5.annotation.Internal; /** * {@link IOSession} event listener. * * @since 5.0 */ @Internal public interface IOSessionListener { void connected(IOSession session); void startTls(IOSession session); void inputReady(IOSession session); void outputReady(IOSession session); void timeout(IOSession session); void exception(IOSession session, Exception ex); void disconnected(IOSession session); } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSessionImpl.java0100664 0000000 0000000 00000020731 14435411677 024615 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.util.Deque; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; class IOSessionImpl implements IOSession { /** Counts instances created. */ private final static AtomicLong COUNT = new AtomicLong(0); private final SelectionKey key; private final SocketChannel channel; private final Deque commandQueue; private final Lock lock; private final String id; private final AtomicReference handlerRef; private final AtomicReference status; private volatile Timeout socketTimeout; private volatile long lastReadTime; private volatile long lastWriteTime; private volatile long lastEventTime; public IOSessionImpl(final String type, final SelectionKey key, final SocketChannel socketChannel) { super(); this.key = Args.notNull(key, "Selection key"); this.channel = Args.notNull(socketChannel, "Socket channel"); this.commandQueue = new ConcurrentLinkedDeque<>(); this.lock = new ReentrantLock(); this.socketTimeout = Timeout.DISABLED; this.id = String.format(type + "-%010d", COUNT.getAndIncrement()); this.handlerRef = new AtomicReference<>(); this.status = new AtomicReference<>(Status.ACTIVE); final long currentTimeMillis = System.currentTimeMillis(); this.lastReadTime = currentTimeMillis; this.lastWriteTime = currentTimeMillis; this.lastEventTime = currentTimeMillis; } @Override public String getId() { return id; } @Override public IOEventHandler getHandler() { return handlerRef.get(); } @Override public void upgrade(final IOEventHandler handler) { handlerRef.set(handler); } @Override public Lock getLock() { return lock; } @Override public void enqueue(final Command command, final Command.Priority priority) { if (priority == Command.Priority.IMMEDIATE) { commandQueue.addFirst(command); } else { commandQueue.add(command); } setEvent(SelectionKey.OP_WRITE); if (isStatusClosed()) { command.cancel(); } } @Override public boolean hasCommands() { return !commandQueue.isEmpty(); } @Override public Command poll() { return commandQueue.poll(); } @Override public ByteChannel channel() { return this.channel; } @Override public SocketAddress getLocalAddress() { return this.channel.socket().getLocalSocketAddress(); } @Override public SocketAddress getRemoteAddress() { return this.channel.socket().getRemoteSocketAddress(); } @Override public int getEventMask() { return this.key.interestOps(); } @Override public void setEventMask(final int newValue) { lock.lock(); try { if (isStatusClosed()) { return; } this.key.interestOps(newValue); } finally { lock.unlock(); } this.key.selector().wakeup(); } @Override public void setEvent(final int op) { lock.lock(); try { if (isStatusClosed()) { return; } this.key.interestOps(this.key.interestOps() | op); } finally { lock.unlock(); } this.key.selector().wakeup(); } @Override public void clearEvent(final int op) { lock.lock(); try { if (isStatusClosed()) { return; } this.key.interestOps(this.key.interestOps() & ~op); } finally { lock.unlock(); } this.key.selector().wakeup(); } @Override public Timeout getSocketTimeout() { return this.socketTimeout; } @Override public void setSocketTimeout(final Timeout timeout) { this.socketTimeout = Timeout.defaultsToDisabled(timeout); this.lastEventTime = System.currentTimeMillis(); } @Override public int read(final ByteBuffer dst) throws IOException { return this.channel.read(dst); } @Override public int write(final ByteBuffer src) throws IOException { return this.channel.write(src); } @Override public void updateReadTime() { lastReadTime = System.currentTimeMillis(); lastEventTime = lastReadTime; } @Override public void updateWriteTime() { lastWriteTime = System.currentTimeMillis(); lastEventTime = lastWriteTime; } @Override public long getLastReadTime() { return lastReadTime; } @Override public long getLastWriteTime() { return lastWriteTime; } @Override public long getLastEventTime() { return lastEventTime; } @Override public Status getStatus() { return this.status.get(); } private boolean isStatusClosed() { return this.status.get() == Status.CLOSED; } @Override public boolean isOpen() { return this.status.get() == Status.ACTIVE && this.channel.isOpen(); } @Override public void close() { close(CloseMode.GRACEFUL); } @Override public void close(final CloseMode closeMode) { if (this.status.compareAndSet(Status.ACTIVE, Status.CLOSED)) { if (closeMode == CloseMode.IMMEDIATE) { try { this.channel.socket().setSoLinger(true, 0); } catch (final SocketException e) { // Quietly ignore } } this.key.cancel(); this.key.attach(null); Closer.closeQuietly(this.key.channel()); if (this.key.selector().isOpen()) { this.key.selector().wakeup(); } } } private static void formatOps(final StringBuilder buffer, final int ops) { if ((ops & SelectionKey.OP_READ) > 0) { buffer.append('r'); } if ((ops & SelectionKey.OP_WRITE) > 0) { buffer.append('w'); } if ((ops & SelectionKey.OP_ACCEPT) > 0) { buffer.append('a'); } if ((ops & SelectionKey.OP_CONNECT) > 0) { buffer.append('c'); } } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append(id).append("["); buffer.append(this.status); buffer.append("]["); if (this.key.isValid()) { formatOps(buffer, this.key.interestOps()); buffer.append(":"); formatOps(buffer, this.key.readyOps()); } buffer.append("]"); return buffer.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSessionRequest.java0100664 0000000 0000000 00000010273 14245617503 025337 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.net.SocketAddress; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.util.Timeout; final class IOSessionRequest implements Future { final NamedEndpoint remoteEndpoint; final SocketAddress remoteAddress; final SocketAddress localAddress; final Timeout timeout; final Object attachment; final BasicFuture future; private final AtomicReference closeableRef; public IOSessionRequest( final NamedEndpoint remoteEndpoint, final SocketAddress remoteAddress, final SocketAddress localAddress, final Timeout timeout, final Object attachment, final FutureCallback callback) { super(); this.remoteEndpoint = remoteEndpoint; this.remoteAddress = remoteAddress; this.localAddress = localAddress; this.timeout = timeout; this.attachment = attachment; this.future = new BasicFuture<>(callback); this.closeableRef = new AtomicReference<>(); } public void completed(final ProtocolIOSession ioSession) { future.completed(ioSession); closeableRef.set(null); } public void failed(final Exception cause) { future.failed(cause); closeableRef.set(null); } public boolean cancel() { final boolean cancelled = future.cancel(); final ModalCloseable closeable = closeableRef.getAndSet(null); if (cancelled && closeable != null) { closeable.close(CloseMode.IMMEDIATE); } return cancelled; } @Override public boolean cancel(final boolean mayInterruptIfRunning) { return cancel(); } @Override public boolean isCancelled() { return future.isCancelled(); } public void assign(final ModalCloseable closeable) { closeableRef.set(closeable); } @Override public boolean isDone() { return future.isDone(); } @Override public IOSession get() throws InterruptedException, ExecutionException { return future.get(); } @Override public IOSession get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return future.get(timeout, unit); } @Override public String toString() { return "[" + "remoteEndpoint=" + remoteEndpoint + ", remoteAddress=" + remoteAddress + ", localAddress=" + localAddress + ", attachment=" + attachment + ']'; } } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOReactor.java0100664 0000000 0000000 00000006302 14403631147 023734 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.TimeValue; /** * HttpCore NIO is based on the Reactor pattern as described by Doug Lea. * The purpose of I/O reactors is to react to I/O events and to dispatch event * notifications to individual I/O sessions. The main idea of I/O reactor * pattern is to break away from the one thread per connection model imposed * by the classic blocking I/O model. *

* The IOReactor interface represents an abstract object implementing * the Reactor pattern. *

* I/O reactors usually employ a small number of dispatch threads (often as * few as one) to dispatch I/O event notifications to a much greater number * (often as many as several thousands) of I/O sessions or connections. It is * generally recommended to have one dispatch thread per CPU core. * * @since 4.0 */ public interface IOReactor extends ModalCloseable { /** * Shuts down the I/O reactor either gracefully or immediately. * During graceful shutdown individual I/O sessions should be * informed about imminent termination and be given a grace period * to complete the ongoing I/O sessions. During immediate shutdown * all ongoing I/O sessions get aborted immediately. */ @Override void close(CloseMode closeMode); /** * Returns the current status of the reactor. * * @return reactor status. */ IOReactorStatus getStatus(); /** * Initiates shutdown of the reactor without blocking. The reactor is expected * to terminate all active connections, to shut down itself and to release * system resources it currently holds * * @since 5.0 */ void initiateShutdown(); /** * Blocks for the given period of time in milliseconds awaiting * the completion of the reactor shutdown. * * @param waitTime wait time. * * @since 5.0 */ void awaitShutdown(TimeValue waitTime) throws InterruptedException; } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOReactorShutdownException.java0100664 0000000 0000000 00000003023 14245617503 027350 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; /** * Signals the I/O reactor has been shut down or is in the process of shutting down. * * @since 5.0 */ public class IOReactorShutdownException extends IllegalStateException { private static final long serialVersionUID = 1L; public IOReactorShutdownException(final String message) { super(message); } } httpcore5/src/main/java/org/apache/hc/core5/reactor/ListenerEndpointRequest.java0100664 0000000 0000000 00000004353 14245617503 026754 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.Closeable; import java.net.SocketAddress; import org.apache.hc.core5.concurrent.BasicFuture; final class ListenerEndpointRequest implements Closeable { final SocketAddress address; final Object attachment; final BasicFuture future; ListenerEndpointRequest(final SocketAddress address, final Object attachment, final BasicFuture future) { this.address = address; this.attachment = attachment; this.future = future; } public void completed(final ListenerEndpoint endpoint) { if (future != null) { future.completed(endpoint); } } public void failed(final Exception cause) { if (future != null) { future.failed(cause); } } public void cancel() { if (future != null) { future.cancel(); } } public boolean isCancelled() { return future != null && future.isCancelled(); } @Override public void close() { cancel(); } } httpcore5/src/main/java/org/apache/hc/core5/reactor/ProtocolUpgradeHandler.java0100664 0000000 0000000 00000003245 14403631147 026517 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.FutureCallback; /** * Application protocol upgrade handler. This routine can be used to upgrade * I/O sessions to a new application protocol. * * @since 5.2 */ @Internal public interface ProtocolUpgradeHandler { /** * Upgrades application protocol of the given I/O session. */ void upgrade(ProtocolIOSession ioSession, FutureCallback callback); } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOEventHandlerFactory.java0100664 0000000 0000000 00000002775 14245617503 026262 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import org.apache.hc.core5.annotation.Internal; /** * Factory interface to create {@link IOEventHandler} instances for the given connected endpoints. * * @since 5.0 */ @Internal public interface IOEventHandlerFactory { IOEventHandler createHandler(ProtocolIOSession ioSession, Object attachment); } httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSession.java0100664 0000000 0000000 00000014271 14245617503 023770 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.net.SocketAddress; import java.nio.channels.ByteChannel; import java.util.concurrent.locks.Lock; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.SocketModalCloseable; import org.apache.hc.core5.util.Identifiable; import org.apache.hc.core5.util.Timeout; /** * IOSession interface represents a sequence of logically related data exchanges * between two end points. *

* The channel associated with implementations of this interface can be used to * read data from and write data to the session. *

* I/O sessions are not bound to an execution thread, therefore one cannot use * the context of the thread to store a session's state. All details about * a particular session must be stored within the session itself, usually * using execution context associated with it. *

* Implementations of this interface are expected to be threading safe. * * @since 4.0 */ @Internal public interface IOSession extends ByteChannel, SocketModalCloseable, Identifiable { /** * This enum represents a set of states I/O session transitions through * during its life-span. */ enum Status { ACTIVE, CLOSING, CLOSED } /** * Returns event handler associated with the session. * * @since 5.0 */ IOEventHandler getHandler(); /** * Upgrades event handler associated with the session. * * @since 5.0 */ void upgrade(IOEventHandler handler); /** * Returns session lock that should be used by I/O event handlers * to synchronize access to the session. * * @since 5.0 */ Lock getLock(); /** * Inserts {@link Command} at the end of the command queue. * * @since 5.0 */ void enqueue(Command command, Command.Priority priority); /** * Tests if there enqueued commands pending execution. * * @since 5.0 */ boolean hasCommands(); /** * Removes first {@link Command} from the command queue if available. * * @since 5.0 */ Command poll(); /** * Returns the underlying I/O channel associated with this session. * * @return the I/O channel. */ ByteChannel channel(); /** * Returns address of the remote peer. * * @return socket address. */ SocketAddress getRemoteAddress(); /** * Returns local address. * * @return socket address. */ SocketAddress getLocalAddress(); /** * Returns mask of I/O evens this session declared interest in. * * @return I/O event mask. */ int getEventMask(); /** * Declares interest in I/O event notifications by setting the event mask * associated with the session * * @param ops new I/O event mask. */ void setEventMask(int ops); /** * Declares interest in a particular I/O event type by updating the event * mask associated with the session. * * @param op I/O event type. */ void setEvent(int op); /** * Clears interest in a particular I/O event type by updating the event * mask associated with the session. * * @param op I/O event type. */ void clearEvent(int op); /** * Terminates the session gracefully and closes the underlying I/O channel. * This method ensures that session termination handshake, such as the one * used by the SSL/TLS protocol, is correctly carried out. */ @Override void close(); /** * Returns status of the session: *

* {@link Status#ACTIVE}: session is active. *

* {@link Status#CLOSING}: session is being closed. *

* {@link Status#CLOSED}: session has been terminated. * * @return session status. */ Status getStatus(); /** * Returns value of the socket timeout in milliseconds. The value of * {@code 0} signifies the session cannot time out. * * @return socket timeout. */ @Override Timeout getSocketTimeout(); /** * Sets value of the socket timeout in milliseconds. The value of * {@code 0} signifies the session cannot time out. *

* Please note this operation may affect the last event time. * * @see #getLastEventTime() * * @param timeout socket timeout. */ @Override void setSocketTimeout(Timeout timeout); /** * Returns timestamp of the last read event. * * @return timestamp. */ long getLastReadTime(); /** * Returns timestamp of the last write event. * * @return timestamp. */ long getLastWriteTime(); /** * Returns timestamp of the last I/O event including socket timeout reset. * * @see #getSocketTimeout() * * @return timestamp. */ long getLastEventTime(); /** * Updates the timestamp of the last read event */ void updateReadTime(); /** * Updates the timestamp of the last write event */ void updateWriteTime(); } httpcore5/src/main/java/org/apache/hc/core5/reactor/SocksProxyProtocolHandlerFactory.java0100664 0000000 0000000 00000004766 14403631147 030615 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; /** * @deprecated Use {@link IOReactorConfig}. * * As of version 5.0.1 {@link #createHandler(ProtocolIOSession, Object)} throws {@link UnsupportedOperationException}. */ @Deprecated public class SocksProxyProtocolHandlerFactory implements IOEventHandlerFactory { private final InetSocketAddress targetAddress; private final String username; private final String password; private final IOEventHandlerFactory eventHandlerFactory; public SocksProxyProtocolHandlerFactory(final SocketAddress targetAddress, final String username, final String password, final IOEventHandlerFactory eventHandlerFactory) throws IOException { this.eventHandlerFactory = eventHandlerFactory; this.username = username; this.password = password; if (targetAddress instanceof InetSocketAddress) { this.targetAddress = (InetSocketAddress) targetAddress; } else { throw new IOException("Unsupported target address type for SOCKS proxy connection: " + targetAddress.getClass()); } } @Override public IOEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { throw new UnsupportedOperationException(); } } httpcore5/src/main/java/org/apache/hc/core5/reactor/SingleCoreIOReactor.java0100664 0000000 0000000 00000036363 14443061705 025722 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.UnknownHostException; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.Timeout; class SingleCoreIOReactor extends AbstractSingleCoreIOReactor implements ConnectionInitiator { private static final int MAX_CHANNEL_REQUESTS = 10000; private final IOEventHandlerFactory eventHandlerFactory; private final IOReactorConfig reactorConfig; private final Decorator ioSessionDecorator; private final IOSessionListener sessionListener; private final Callback sessionShutdownCallback; private final Queue closedSessions; private final Queue channelQueue; private final Queue requestQueue; private final AtomicBoolean shutdownInitiated; private final long selectTimeoutMillis; private volatile long lastTimeoutCheckMillis; SingleCoreIOReactor( final Callback exceptionCallback, final IOEventHandlerFactory eventHandlerFactory, final IOReactorConfig reactorConfig, final Decorator ioSessionDecorator, final IOSessionListener sessionListener, final Callback sessionShutdownCallback) { super(exceptionCallback); this.eventHandlerFactory = Args.notNull(eventHandlerFactory, "Event handler factory"); this.reactorConfig = Args.notNull(reactorConfig, "I/O reactor config"); this.ioSessionDecorator = ioSessionDecorator; this.sessionListener = sessionListener; this.sessionShutdownCallback = sessionShutdownCallback; this.shutdownInitiated = new AtomicBoolean(false); this.closedSessions = new ConcurrentLinkedQueue<>(); this.channelQueue = new ConcurrentLinkedQueue<>(); this.requestQueue = new ConcurrentLinkedQueue<>(); this.selectTimeoutMillis = this.reactorConfig.getSelectInterval().toMilliseconds(); } void enqueueChannel(final ChannelEntry entry) throws IOReactorShutdownException { if (getStatus().compareTo(IOReactorStatus.ACTIVE) > 0) { throw new IOReactorShutdownException("I/O reactor has been shut down"); } this.channelQueue.add(entry); this.selector.wakeup(); } @Override void doTerminate() { closePendingChannels(); closePendingConnectionRequests(); processClosedSessions(); } @Override void doExecute() throws IOException { while (!Thread.currentThread().isInterrupted()) { final int readyCount = this.selector.select(this.selectTimeoutMillis); if (getStatus().compareTo(IOReactorStatus.SHUTTING_DOWN) >= 0) { if (this.shutdownInitiated.compareAndSet(false, true)) { initiateSessionShutdown(); } closePendingChannels(); } if (getStatus() == IOReactorStatus.SHUT_DOWN) { break; } // Process selected I/O events if (readyCount > 0) { processEvents(this.selector.selectedKeys()); } validateActiveChannels(); // Process closed sessions processClosedSessions(); // If active process new channels if (getStatus() == IOReactorStatus.ACTIVE) { processPendingChannels(); processPendingConnectionRequests(); } // Exit select loop if graceful shutdown has been completed if (getStatus() == IOReactorStatus.SHUTTING_DOWN && this.selector.keys().isEmpty()) { break; } if (getStatus() == IOReactorStatus.SHUT_DOWN) { break; } } } private void initiateSessionShutdown() { if (this.sessionShutdownCallback != null) { final Set keys = this.selector.keys(); for (final SelectionKey key : keys) { final InternalChannel channel = (InternalChannel) key.attachment(); if (channel instanceof InternalDataChannel) { this.sessionShutdownCallback.execute((InternalDataChannel) channel); } } } } private void validateActiveChannels() { final long currentTimeMillis = System.currentTimeMillis(); if ((currentTimeMillis - this.lastTimeoutCheckMillis) >= this.selectTimeoutMillis) { this.lastTimeoutCheckMillis = currentTimeMillis; for (final SelectionKey key : this.selector.keys()) { checkTimeout(key, currentTimeMillis); } } } private void processEvents(final Set selectedKeys) { for (final SelectionKey key : selectedKeys) { final InternalChannel channel = (InternalChannel) key.attachment(); if (channel != null) { try { channel.handleIOEvent(key.readyOps()); } catch (final CancelledKeyException ex) { channel.close(CloseMode.GRACEFUL); } } } selectedKeys.clear(); } private void processPendingChannels() throws IOException { ChannelEntry entry; for (int i = 0; i < MAX_CHANNEL_REQUESTS && (entry = this.channelQueue.poll()) != null; i++) { final SocketChannel socketChannel = entry.channel; final Object attachment = entry.attachment; try { prepareSocket(socketChannel.socket()); socketChannel.configureBlocking(false); } catch (final IOException ex) { logException(ex); try { socketChannel.close(); } catch (final IOException ex2) { logException(ex2); } throw ex; } final SelectionKey key; try { key = socketChannel.register(this.selector, SelectionKey.OP_READ); } catch (final ClosedChannelException ex) { return; } final IOSession ioSession = new IOSessionImpl("a", key, socketChannel); final InternalDataChannel dataChannel = new InternalDataChannel( ioSession, null, ioSessionDecorator, sessionListener, closedSessions); dataChannel.setSocketTimeout(this.reactorConfig.getSoTimeout()); dataChannel.upgrade(this.eventHandlerFactory.createHandler(dataChannel, attachment)); key.attach(dataChannel); dataChannel.handleIOEvent(SelectionKey.OP_CONNECT); } } private void processClosedSessions() { for (;;) { final InternalDataChannel dataChannel = this.closedSessions.poll(); if (dataChannel == null) { break; } try { dataChannel.disconnected(); } catch (final CancelledKeyException ex) { // ignore and move on } } } private void checkTimeout(final SelectionKey key, final long nowMillis) { final InternalChannel channel = (InternalChannel) key.attachment(); if (channel != null) { channel.checkTimeout(nowMillis); } } @Override public Future connect( final NamedEndpoint remoteEndpoint, final SocketAddress remoteAddress, final SocketAddress localAddress, final Timeout timeout, final Object attachment, final FutureCallback callback) throws IOReactorShutdownException { Args.notNull(remoteEndpoint, "Remote endpoint"); final IOSessionRequest sessionRequest = new IOSessionRequest( remoteEndpoint, remoteAddress != null ? remoteAddress : new InetSocketAddress(remoteEndpoint.getHostName(), remoteEndpoint.getPort()), localAddress, timeout, attachment, callback); this.requestQueue.add(sessionRequest); this.selector.wakeup(); return sessionRequest; } private void prepareSocket(final Socket socket) throws IOException { socket.setTcpNoDelay(this.reactorConfig.isTcpNoDelay()); socket.setKeepAlive(this.reactorConfig.isSoKeepAlive()); if (this.reactorConfig.getSndBufSize() > 0) { socket.setSendBufferSize(this.reactorConfig.getSndBufSize()); } if (this.reactorConfig.getRcvBufSize() > 0) { socket.setReceiveBufferSize(this.reactorConfig.getRcvBufSize()); } if (this.reactorConfig.getTrafficClass() > 0) { socket.setTrafficClass(this.reactorConfig.getTrafficClass()); } final int linger = this.reactorConfig.getSoLinger().toSecondsIntBound(); if (linger >= 0) { socket.setSoLinger(true, linger); } } private void validateAddress(final SocketAddress address) throws UnknownHostException { if (address instanceof InetSocketAddress) { final InetSocketAddress endpoint = (InetSocketAddress) address; if (endpoint.isUnresolved()) { throw new UnknownHostException(endpoint.getHostName()); } } } private void processPendingConnectionRequests() { IOSessionRequest sessionRequest; for (int i = 0; i < MAX_CHANNEL_REQUESTS && (sessionRequest = this.requestQueue.poll()) != null; i++) { if (!sessionRequest.isCancelled()) { final SocketChannel socketChannel; try { socketChannel = SocketChannel.open(); } catch (final IOException ex) { sessionRequest.failed(ex); return; } try { processConnectionRequest(socketChannel, sessionRequest); } catch (final IOException | RuntimeException ex) { Closer.closeQuietly(socketChannel); sessionRequest.failed(ex); } } } } private void processConnectionRequest(final SocketChannel socketChannel, final IOSessionRequest sessionRequest) throws IOException { socketChannel.configureBlocking(false); prepareSocket(socketChannel.socket()); validateAddress(sessionRequest.localAddress); if (sessionRequest.localAddress != null) { final Socket sock = socketChannel.socket(); sock.setReuseAddress(this.reactorConfig.isSoReuseAddress()); sock.bind(sessionRequest.localAddress); } final SocketAddress socksProxyAddress = reactorConfig.getSocksProxyAddress(); final SocketAddress remoteAddress = socksProxyAddress != null ? socksProxyAddress : sessionRequest.remoteAddress; // Run this under a doPrivileged to support lib users that run under a SecurityManager this allows granting connect permissions // only to this library validateAddress(remoteAddress); final boolean connected; try { connected = AccessController.doPrivileged( (PrivilegedExceptionAction) () -> socketChannel.connect(remoteAddress)); } catch (final PrivilegedActionException e) { Asserts.check(e.getCause() instanceof IOException, "method contract violation only checked exceptions are wrapped: " + e.getCause()); // only checked exceptions are wrapped - error and RTExceptions are rethrown by doPrivileged throw (IOException) e.getCause(); } final SelectionKey key = socketChannel.register(this.selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ); final IOSession ioSession = new IOSessionImpl("c", key, socketChannel); final InternalDataChannel dataChannel = new InternalDataChannel( ioSession, sessionRequest.remoteEndpoint, ioSessionDecorator, sessionListener, closedSessions); dataChannel.setSocketTimeout(reactorConfig.getSoTimeout()); final InternalChannel connectChannel = new InternalConnectChannel( key, socketChannel, sessionRequest, dataChannel, eventHandlerFactory, reactorConfig); if (connected) { connectChannel.handleIOEvent(SelectionKey.OP_CONNECT); } else { key.attach(connectChannel); sessionRequest.assign(connectChannel); } } private void closePendingChannels() { ChannelEntry entry; while ((entry = this.channelQueue.poll()) != null) { final SocketChannel socketChannel = entry.channel; try { socketChannel.close(); } catch (final IOException ex) { logException(ex); } } } private void closePendingConnectionRequests() { IOSessionRequest sessionRequest; while ((sessionRequest = this.requestQueue.poll()) != null) { sessionRequest.cancel(); } } } httpcore5/src/main/java/org/apache/hc/core5/reactor/Command.java0100664 0000000 0000000 00000002753 14245617503 023475 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import org.apache.hc.core5.concurrent.Cancellable; /** * Abstract command {@link IOSession} can act upon. Pending commands * can be cancelled with {@link Cancellable#cancel()}. * * @since 5.0 */ public interface Command extends Cancellable { enum Priority { NORMAL, IMMEDIATE } } httpcore5/src/main/java/org/apache/hc/core5/reactor/ProtocolIOSession.java0100664 0000000 0000000 00000004632 14403631147 025506 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; /** * TLS capable, protocol upgradable {@link IOSession}. * * @since 5.0 */ public interface ProtocolIOSession extends IOSession, TransportSecurityLayer { /** * Switches this I/O session to the application protocol with the given ID. * @param protocolId the application protocol ID * @param callback the result callback * @throws UnsupportedOperationException if application protocol switch * is not supported. */ default void switchProtocol(String protocolId, FutureCallback callback) throws UnsupportedOperationException { throw new UnsupportedOperationException("Protocol switch not supported"); } /** * Registers protocol upgrade handler with the given application protocol ID. * * @since 5.2 * @param protocolId the application protocol ID * @param upgradeHandler the upgrade handler. * * @since 5.2 */ default void registerProtocol(String protocolId, ProtocolUpgradeHandler upgradeHandler) { } NamedEndpoint getInitialEndpoint(); } httpcore5/src/main/java/org/apache/hc/core5/reactor/ConnectionInitiator.java0100664 0000000 0000000 00000006620 14245617503 026076 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.net.SocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.util.Timeout; /** * Non-blocking connection initiator. * * @since 5.0 */ public interface ConnectionInitiator { /** * Requests a connection to a remote host. *

* Opening a connection to a remote host usually tends to be a time * consuming process and may take a while to complete. One can monitor and * control the process of session initialization by means of the * {@link Future} interface. *

* There are several parameters one can use to exert a greater control over * the process of session initialization: *

* A non-null local socket address parameter can be used to bind the socket * to a specific local address. *

* An attachment object can added to the new session's context upon * initialization. This object can be used to pass an initial processing * state to the protocol handler. *

* It is often desirable to be able to react to the completion of a session * request asynchronously without having to wait for it, blocking the * current thread of execution. One can optionally provide an implementation * {@link FutureCallback} instance to get notified of events related * to session requests, such as request completion, cancellation, failure or * timeout. * * @param remoteEndpoint name of the remote host. * @param remoteAddress remote socket address. * @param localAddress local socket address. Can be {@code null}, * in which can the default local address and a random port will be used. * @param timeout connect timeout. * @param attachment the attachment object. Can be {@code null}. * @param callback interface. Can be {@code null}. * @return session request object. */ Future connect( NamedEndpoint remoteEndpoint, SocketAddress remoteAddress, SocketAddress localAddress, Timeout timeout, Object attachment, FutureCallback callback); } httpcore5/src/main/java/org/apache/hc/core5/reactor/MultiCoreIOReactor.java0100664 0000000 0000000 00000013407 14403631147 025564 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; class MultiCoreIOReactor implements IOReactor { private final IOReactor[] ioReactors; private final Thread[] threads; private final AtomicReference status; private final AtomicBoolean terminated; MultiCoreIOReactor(final IOReactor[] ioReactors, final Thread[] threads) { super(); this.ioReactors = ioReactors.clone(); this.threads = threads.clone(); this.status = new AtomicReference<>(IOReactorStatus.INACTIVE); this.terminated = new AtomicBoolean(); } @Override public IOReactorStatus getStatus() { return this.status.get(); } /** * Activates all worker I/O reactors. * The I/O main reactor will start reacting to I/O events and triggering * notification methods. The worker I/O reactor in their turn will start * reacting to I/O events and dispatch I/O event notifications to the * {@link IOEventHandler} associated with the given I/O session. */ public final void start() { if (this.status.compareAndSet(IOReactorStatus.INACTIVE, IOReactorStatus.ACTIVE)) { for (int i = 0; i < this.threads.length; i++) { this.threads[i].start(); } } } @Override public final void initiateShutdown() { if (this.status.compareAndSet(IOReactorStatus.INACTIVE, IOReactorStatus.SHUT_DOWN) || this.status.compareAndSet(IOReactorStatus.ACTIVE, IOReactorStatus.SHUTTING_DOWN)) { for (int i = 0; i < this.ioReactors.length; i++) { final IOReactor ioReactor = this.ioReactors[i]; ioReactor.initiateShutdown(); } } } @Override public final void awaitShutdown(final TimeValue waitTime) throws InterruptedException { Args.notNull(waitTime, "Wait time"); final long deadline = System.currentTimeMillis() + waitTime.toMilliseconds(); long remaining = waitTime.toMilliseconds(); for (int i = 0; i < this.ioReactors.length; i++) { final IOReactor ioReactor = this.ioReactors[i]; if (ioReactor.getStatus().compareTo(IOReactorStatus.SHUT_DOWN) < 0) { ioReactor.awaitShutdown(TimeValue.of(remaining, TimeUnit.MILLISECONDS)); remaining = deadline - System.currentTimeMillis(); if (remaining <= 0) { return; } } } for (int i = 0; i < this.threads.length; i++) { final Thread thread = this.threads[i]; thread.join(remaining); remaining = deadline - System.currentTimeMillis(); if (remaining <= 0) { return; } } } @Override public final void close(final CloseMode closeMode) { close(closeMode, Timeout.ofSeconds(5)); } /** * Shuts down the I/O reactor either gracefully or immediately. * During graceful shutdown individual I/O sessions should be * informed about imminent termination and be given a grace period * to complete the ongoing I/O sessions. During immediate shutdown * all ongoing I/O sessions get aborted immediately. * * @param closeMode How to close the IO reactor. * @param timeout How long to wait for the IO reactor to close gracefully. * @since 5.2 */ public void close(final CloseMode closeMode, final Timeout timeout) { if (closeMode == CloseMode.GRACEFUL) { initiateShutdown(); try { awaitShutdown(timeout); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } } this.status.set(IOReactorStatus.SHUT_DOWN); if (this.terminated.compareAndSet(false, true)) { for (int i = 0; i < this.ioReactors.length; i++) { Closer.close(this.ioReactors[i], CloseMode.IMMEDIATE); } for (int i = 0; i < this.threads.length; i++) { this.threads[i].interrupt(); } } } @Override public final void close() { close(CloseMode.GRACEFUL); } @Override public String toString() { return getClass().getSimpleName() + " [status=" + status + "]"; } } httpcore5/src/main/java/org/apache/hc/core5/reactor/DefaultListeningIOReactor.java0100664 0000000 0000000 00000017271 14403631147 027125 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.net.SocketAddress; import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; /** * Multi-core I/O reactor that can act as both {@link ConnectionInitiator} * and {@link ConnectionAcceptor}. Internally this I/O reactor distributes newly created * I/O session equally across multiple I/O worker threads for a more optimal resource * utilization and a better I/O performance. Usually it is recommended to have * one worker I/O reactor per physical CPU core. * * @since 4.0 */ public class DefaultListeningIOReactor extends AbstractIOReactorBase implements ConnectionAcceptor { private final static ThreadFactory DISPATCH_THREAD_FACTORY = new DefaultThreadFactory("I/O server dispatch", true); private final static ThreadFactory LISTENER_THREAD_FACTORY = new DefaultThreadFactory("I/O listener", true); private final int workerCount; private final SingleCoreIOReactor[] workers; private final SingleCoreListeningIOReactor listener; private final MultiCoreIOReactor ioReactor; private final IOWorkers.Selector workerSelector; /** * Creates an instance of DefaultListeningIOReactor with the given configuration. * * @param eventHandlerFactory the factory to create I/O event handlers. * @param ioReactorConfig I/O reactor configuration. * @param listenerThreadFactory the factory to create listener thread. * Can be {@code null}. * * @since 5.0 */ public DefaultListeningIOReactor( final IOEventHandlerFactory eventHandlerFactory, final IOReactorConfig ioReactorConfig, final ThreadFactory dispatchThreadFactory, final ThreadFactory listenerThreadFactory, final Decorator ioSessionDecorator, final Callback exceptionCallback, final IOSessionListener sessionListener, final Callback sessionShutdownCallback) { Args.notNull(eventHandlerFactory, "Event handler factory"); this.workerCount = ioReactorConfig != null ? ioReactorConfig.getIoThreadCount() : IOReactorConfig.DEFAULT.getIoThreadCount(); this.workers = new SingleCoreIOReactor[workerCount]; final Thread[] threads = new Thread[workerCount + 1]; for (int i = 0; i < this.workers.length; i++) { final SingleCoreIOReactor dispatcher = new SingleCoreIOReactor( exceptionCallback, eventHandlerFactory, ioReactorConfig != null ? ioReactorConfig : IOReactorConfig.DEFAULT, ioSessionDecorator, sessionListener, sessionShutdownCallback); this.workers[i] = dispatcher; threads[i + 1] = (dispatchThreadFactory != null ? dispatchThreadFactory : DISPATCH_THREAD_FACTORY).newThread(new IOReactorWorker(dispatcher)); } final IOReactor[] ioReactors = new IOReactor[this.workerCount + 1]; System.arraycopy(this.workers, 0, ioReactors, 1, this.workerCount); this.listener = new SingleCoreListeningIOReactor(exceptionCallback, ioReactorConfig, this::enqueueChannel); ioReactors[0] = this.listener; threads[0] = (listenerThreadFactory != null ? listenerThreadFactory : LISTENER_THREAD_FACTORY).newThread(new IOReactorWorker(listener)); this.ioReactor = new MultiCoreIOReactor(ioReactors, threads); workerSelector = IOWorkers.newSelector(workers); } /** * Creates an instance of DefaultListeningIOReactor with the given configuration. * * @param eventHandlerFactory the factory to create I/O event handlers. * @param config I/O reactor configuration. * Can be {@code null}. * * @since 5.0 */ public DefaultListeningIOReactor( final IOEventHandlerFactory eventHandlerFactory, final IOReactorConfig config, final Callback sessionShutdownCallback) { this(eventHandlerFactory, config, null, null, null, null, null, sessionShutdownCallback); } /** * Creates an instance of DefaultListeningIOReactor with default configuration. * * @param eventHandlerFactory the factory to create I/O event handlers. * * @since 5.0 */ public DefaultListeningIOReactor(final IOEventHandlerFactory eventHandlerFactory) { this(eventHandlerFactory, null, null); } @Override public void start() { ioReactor.start(); } @Override public Future listen( final SocketAddress address, final Object attachment, final FutureCallback callback) { return listener.listen(address, attachment, callback); } @Override public Future listen(final SocketAddress address, final FutureCallback callback) { return listen(address, null, callback); } public Future listen(final SocketAddress address) { return listen(address, null); } @Override public Set getEndpoints() { return listener.getEndpoints(); } @Override public void pause() throws IOException { listener.pause(); } @Override public void resume() throws IOException { listener.resume(); } @Override public IOReactorStatus getStatus() { return ioReactor.getStatus(); } @Override IOWorkers.Selector getWorkerSelector() { return workerSelector; } private void enqueueChannel(final ChannelEntry entry) { try { workerSelector.next().enqueueChannel(entry); } catch (final IOReactorShutdownException ex) { initiateShutdown(); } } @Override public void initiateShutdown() { ioReactor.initiateShutdown(); } @Override public void awaitShutdown(final TimeValue waitTime) throws InterruptedException { ioReactor.awaitShutdown(waitTime); } @Override public void close(final CloseMode closeMode) { ioReactor.close(closeMode); } @Override public void close() throws IOException { ioReactor.close(); } } httpcore5/src/main/java/org/apache/hc/core5/reactor/AbstractIOSessionPool.java0100664 0000000 0000000 00000026234 14442066205 026304 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.util.ArrayDeque; import java.util.HashSet; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.concurrent.FutureContribution; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public abstract class AbstractIOSessionPool implements ModalCloseable { private final ConcurrentMap sessionPool; private final AtomicBoolean closed; public AbstractIOSessionPool() { super(); this.sessionPool = new ConcurrentHashMap<>(); this.closed = new AtomicBoolean(false); } protected abstract Future connectSession( T namedEndpoint, Timeout connectTimeout, FutureCallback callback); protected abstract void validateSession( IOSession ioSession, Callback callback); protected abstract void closeSession( IOSession ioSession, CloseMode closeMode); @Override public final void close(final CloseMode closeMode) { if (closed.compareAndSet(false, true)) { for (final PoolEntry poolEntry : sessionPool.values()) { synchronized (poolEntry) { if (poolEntry.session != null) { closeSession(poolEntry.session, closeMode); poolEntry.session = null; } if (poolEntry.sessionFuture != null) { poolEntry.sessionFuture.cancel(true); poolEntry.sessionFuture = null; } for (;;) { final FutureCallback callback = poolEntry.requestQueue.poll(); if (callback != null) { callback.cancelled(); } else { break; } } } } sessionPool.clear(); } } @Override public final void close() { close(CloseMode.GRACEFUL); } PoolEntry getPoolEntry(final T endpoint) { PoolEntry poolEntry = sessionPool.get(endpoint); if (poolEntry == null) { final PoolEntry newPoolEntry = new PoolEntry(); poolEntry = sessionPool.putIfAbsent(endpoint, newPoolEntry); if (poolEntry == null) { poolEntry = newPoolEntry; } } return poolEntry; } public final Future getSession( final T endpoint, final Timeout connectTimeout, final FutureCallback callback) { Args.notNull(endpoint, "Endpoint"); Asserts.check(!closed.get(), "Connection pool shut down"); final ComplexFuture future = new ComplexFuture<>(callback); final PoolEntry poolEntry = getPoolEntry(endpoint); getSessionInternal(poolEntry, false, endpoint, connectTimeout, new FutureCallback() { @Override public void completed(final IOSession ioSession) { validateSession(ioSession, result -> { if (result) { future.completed(ioSession); } else { getSessionInternal(poolEntry, true, endpoint, connectTimeout, new FutureContribution(future) { @Override public void completed(final IOSession ioSession1) { future.completed(ioSession1); } }); } }); } @Override public void failed(final Exception ex) { future.failed(ex); } @Override public void cancelled() { future.cancel(); } }); return future; } private void getSessionInternal( final PoolEntry poolEntry, final boolean requestNew, final T namedEndpoint, final Timeout connectTimeout, final FutureCallback callback) { synchronized (poolEntry) { if (poolEntry.session != null && requestNew) { closeSession(poolEntry.session, CloseMode.GRACEFUL); poolEntry.session = null; } if (poolEntry.session != null && !poolEntry.session.isOpen()) { poolEntry.session = null; } if (poolEntry.session != null) { callback.completed(poolEntry.session); } else { poolEntry.requestQueue.add(callback); if (poolEntry.sessionFuture != null && poolEntry.completed) { poolEntry.sessionFuture = null; } if (poolEntry.sessionFuture == null) { poolEntry.completed = false; poolEntry.sessionFuture = connectSession( namedEndpoint, connectTimeout, new FutureCallback() { @Override public void completed(final IOSession result) { synchronized (poolEntry) { poolEntry.completed = true; if (poolEntry.session == null) { poolEntry.session = result; } else { closeSession(result,CloseMode.GRACEFUL); } for (;;) { final FutureCallback callback = poolEntry.requestQueue.poll(); if (callback != null) { callback.completed(result); } else { break; } } } } @Override public void failed(final Exception ex) { synchronized (poolEntry) { poolEntry.completed = true; poolEntry.session = null; for (;;) { final FutureCallback callback = poolEntry.requestQueue.poll(); if (callback != null) { callback.failed(ex); } else { break; } } } } @Override public void cancelled() { failed(new ConnectionClosedException("Connection request cancelled")); } }); } } } } public final void enumAvailable(final Callback callback) { for (final PoolEntry poolEntry: sessionPool.values()) { if (poolEntry.session != null) { synchronized (poolEntry) { if (poolEntry.session != null) { callback.execute(poolEntry.session); if (!poolEntry.session.isOpen()) { poolEntry.session = null; } } } } } } public final void closeIdle(final TimeValue idleTime) { final long deadline = System.currentTimeMillis() - (TimeValue.isPositive(idleTime) ? idleTime.toMilliseconds() : 0); for (final PoolEntry poolEntry: sessionPool.values()) { if (poolEntry.session != null) { synchronized (poolEntry) { if (poolEntry.session != null && poolEntry.session.getLastReadTime() <= deadline) { closeSession(poolEntry.session, CloseMode.GRACEFUL); poolEntry.session = null; } } } } } public final Set getRoutes() { return new HashSet<>(sessionPool.keySet()); } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("I/O sessions: "); buffer.append(sessionPool.size()); return buffer.toString(); } static class PoolEntry { final Queue> requestQueue; volatile boolean completed; volatile Future sessionFuture; volatile IOSession session; PoolEntry() { this.requestQueue = new ArrayDeque<>(); } } } httpcore5/src/main/java/org/apache/hc/core5/reactor/ListenerEndpointImpl.java0100664 0000000 0000000 00000004672 14245617503 026231 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.net.SocketAddress; import java.nio.channels.SelectionKey; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.Closer; class ListenerEndpointImpl implements ListenerEndpoint { private final SelectionKey key; final SocketAddress address; final Object attachment; private final AtomicBoolean closed; public ListenerEndpointImpl(final SelectionKey key, final Object attachment, final SocketAddress address) { super(); this.key = key; this.address = address; this.attachment = attachment; this.closed = new AtomicBoolean(false); } @Override public SocketAddress getAddress() { return this.address; } @Override public String toString() { return "endpoint: " + address; } @Override public boolean isClosed() { return this.closed.get(); } @Override public void close() throws IOException { if (closed.compareAndSet(false, true)) { key.cancel(); key.channel().close(); } } @Override public void close(final CloseMode closeMode) { Closer.closeQuietly(this); } } httpcore5/src/main/java/org/apache/hc/core5/util/0040775 0000000 0000000 00000000000 14435411677 020573 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/util/package-info.java0100664 0000000 0000000 00000002344 14245617503 023755 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core utility classes. */ package org.apache.hc.core5.util; httpcore5/src/main/java/org/apache/hc/core5/util/DeadlineTimeoutException.java0100664 0000000 0000000 00000005263 14245617503 026367 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * A specialization of {@link TimeoutException} that carries a deadline and an actual value, both as UNIX times. * * @since 5.0 */ public class DeadlineTimeoutException extends TimeoutException { private static final long serialVersionUID = 1L; /** * Creates a new exception for the given timeout deadline and actual timeout. * * @param deadline When was the deadline in UNIX time. * @return a new TimeoutValueException. */ public static DeadlineTimeoutException from(final Deadline deadline) { return new DeadlineTimeoutException(deadline); } private final Deadline deadline; /** * Creates a new exception for the given timeout deadline and actual timeout. * * @param deadline When was the deadline in UNIX time. */ private DeadlineTimeoutException(final Deadline deadline) { // Deadline in a format like ISO8601: YYYY-MM-DDThh:mm:ss.sTZD // We use the TimeZone zone ID because we cannot get from String.format() in // Java 7 and the short names have been deprecated. super(deadline.format(TimeUnit.MILLISECONDS)); this.deadline = deadline; } /** * The expected deadline for this timeout since the start of UNIX time. * * @return The expected deadline for this timeout since the start of UNIX time. */ public Deadline getDeadline() { return deadline; } } httpcore5/src/main/java/org/apache/hc/core5/util/Deadline.java0100664 0000000 0000000 00000022163 14403631147 023133 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.text.ParseException; import java.time.Instant; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.util.concurrent.TimeUnit; /** * A deadline based on a UNIX time, the elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January * 1970. * * @since 5.0 */ public class Deadline { /** * The format used for parsing and formatting dates. */ public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; /** * A special internal value that marks a deadline as the longest possible. */ private static final long INTERNAL_MAX_VALUE = Long.MAX_VALUE; /** * A special internal value that marks a deadline as the shortest possible. */ private static final long INTERNAL_MIN_VALUE = 0; /** * The maximum (longest-lived) deadline. */ public static Deadline MAX_VALUE = new Deadline(INTERNAL_MAX_VALUE); /** * The minimum (shortest-lived) deadline. */ public static Deadline MIN_VALUE = new Deadline(INTERNAL_MIN_VALUE); private static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder() .parseLenient() .parseCaseInsensitive() .appendPattern(DATE_FORMAT) .toFormatter(); /** * Calculates a deadline with a given time in milliseconds plus a given time value. Non-positive time values * represent an indefinite timeout without a deadline. * * @param timeMillis A time in UNIX milliseconds, usually the current time. * @param timeValue time value to add to {@code timeMillis}. * @return a deadline representing the current time plus the given time value. */ public static Deadline calculate(final long timeMillis, final TimeValue timeValue) { if (TimeValue.isPositive(timeValue)) { // TODO handle unlikely overflow final long deadline = timeMillis + timeValue.toMilliseconds(); return deadline < 0 ? Deadline.MAX_VALUE : Deadline.fromUnixMilliseconds(deadline); } return Deadline.MAX_VALUE; } /** * Calculates a deadline from now plus a given time value. Non-positive time values * represent an indefinite timeout without a deadline. * * @param timeValue time value to add to {@code timeMillis}. * @return a deadline representing the current time plus the given time value. */ public static Deadline calculate(final TimeValue timeValue) { return calculate(System.currentTimeMillis(), timeValue); } /** * Creates a deadline from a UNIX time in milliseconds. * * @param value a UNIX time in milliseconds. * @return a new deadline. */ public static Deadline fromUnixMilliseconds(final long value) { if (value == INTERNAL_MAX_VALUE) { return MAX_VALUE; } if (value == INTERNAL_MIN_VALUE) { return MIN_VALUE; } return new Deadline(value); } /** * Creates a deadline from a string in the format {@value #DATE_FORMAT}. * * @param source a string in the format {@value #DATE_FORMAT}. * @return a deadline from a string in the format {@value #DATE_FORMAT}. * @throws ParseException if the specified source string cannot be parsed. */ public static Deadline parse(final String source) throws ParseException { if (source == null) { return null; } final Instant instant = Instant.from(DATE_TIME_FORMATTER.parse(source)); return fromUnixMilliseconds(instant.toEpochMilli()); } private volatile boolean frozen; private volatile long lastCheck; /* * Internal representation is a UNIX time. */ private final long value; /** * Constructs a new instance with the given UNIX time in milliseconds. * * @param deadlineMillis UNIX time in milliseconds. */ private Deadline(final long deadlineMillis) { super(); this.value = deadlineMillis; setLastCheck(); } @Override public boolean equals(final Object obj) { // Only take into account the deadline value. if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Deadline other = (Deadline) obj; return value == other.value; } @Override public int hashCode() { // Only take into account the deadline value. return Long.hashCode(value); } /** * Formats this deadline. * * @param overdueTimeUnit the time unit to show how much over the deadline we are. * @return a formatted string. */ public String format(final TimeUnit overdueTimeUnit) { return String.format("Deadline: %s, %s overdue", formatTarget(), TimeValue.of(remaining(), overdueTimeUnit)); } /** * Formats the deadline value as a string in the format {@value #DATE_FORMAT}. * * @return a formatted string in the format {@value #DATE_FORMAT}. */ public String formatTarget() { return DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(value).atOffset(ZoneOffset.UTC)); } public Deadline freeze() { frozen = true; return this; } /** * Package private for testing. * * @return the last time we checked the current time. */ long getLastCheck() { return lastCheck; } /** * Gets the UNIX time deadline value. * * @return the UNIX time deadline value. */ public long getValue() { return value; } /** * Returns whether this deadline occurs before the given time in milliseconds. * * @param millis the time to compare. * @return whether this deadline occurs before the given time in milliseconds. */ public boolean isBefore(final long millis) { return value < millis; } /** * Returns whether the deadline has expired. * * @return whether the deadline has expired. */ public boolean isExpired() { setLastCheck(); return value < this.lastCheck; } /** * Returns whether this deadline is the maximum deadline. * * @return whether this deadline is the maximum deadline. */ public boolean isMax() { return value == INTERNAL_MAX_VALUE; } /** * Returns whether this deadline is the minimum deadline. * * @return whether this deadline is the minimum deadline. */ public boolean isMin() { return value == INTERNAL_MIN_VALUE; } /** * Returns whether this deadline has not expired. * * @return whether this deadline has not expired. */ public boolean isNotExpired() { setLastCheck(); return value >= this.lastCheck; } /** * Returns the smaller of this and another {@code Deadline}. * * @param other another deadline. * @return the smaller of {@code this} and {@code other}. */ public Deadline min(final Deadline other) { return value <= other.value ? this : other; } /** * Returns the difference in milliseconds between the deadline and now. * * @return the different in milliseconds between the deadline and now. */ public long remaining() { setLastCheck(); return value - lastCheck; } /** * Returns the difference as a TimeValue between the deadline and now. * * @return Returns the different as a TimeValue between the deadline and now. */ public TimeValue remainingTimeValue() { return TimeValue.of(remaining(), TimeUnit.MILLISECONDS); } private void setLastCheck() { if (!frozen) { this.lastCheck = System.currentTimeMillis(); }} @Override public String toString() { return formatTarget(); } } httpcore5/src/main/java/org/apache/hc/core5/util/LangUtils.java0100664 0000000 0000000 00000006303 14403631147 023326 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.util.Arrays; import java.util.Objects; /** * A set of utility methods to help produce consistent * {@link Object#equals equals} and {@link Object#hashCode hashCode} methods. * * * @since 4.0 */ public final class LangUtils { public static final int HASH_SEED = 17; public static final int HASH_OFFSET = 37; /** Disabled default constructor. */ private LangUtils() { } public static int hashCode(final int seed, final int hashcode) { return seed * HASH_OFFSET + hashcode; } public static int hashCode(final int seed, final boolean b) { return hashCode(seed, b ? 1 : 0); } public static int hashCode(final int seed, final Object obj) { return hashCode(seed, obj != null ? obj.hashCode() : 0); } /** * Check if two objects are equal. * * @param obj1 first object to compare, may be {@code null} * @param obj2 second object to compare, may be {@code null} * @return {@code true} if the objects are equal or both null * @deprecated Use {@link Objects#equals(Object)}. */ @Deprecated public static boolean equals(final Object obj1, final Object obj2) { return Objects.equals(obj1, obj2); } /** * Check if two object arrays are equal. *

    *
  • If both parameters are null, return {@code true}
  • *
  • If one parameter is null, return {@code false}
  • *
  • If the array lengths are different, return {@code false}
  • *
  • Compare array elements using .equals(); return {@code false} if any comparisons fail.
  • *
  • Return {@code true}
  • *
* * @param a1 first array to compare, may be {@code null} * @param a2 second array to compare, may be {@code null} * @return {@code true} if the arrays are equal or both null * @deprecated Use {@link Arrays#equals(Object)}. */ @Deprecated public static boolean equals(final Object[] a1, final Object[] a2) { return Arrays.equals(a1, a2); } } httpcore5/src/main/java/org/apache/hc/core5/util/ByteArrayBuffer.java0100664 0000000 0000000 00000030163 14245617503 024465 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.io.Serializable; import java.nio.ByteBuffer; /** * A resizable byte array. * * @since 4.0 */ public final class ByteArrayBuffer implements Serializable { private static final long serialVersionUID = 4359112959524048036L; private byte[] array; private int len; /** * Creates an instance of {@link ByteArrayBuffer} with the given initial * capacity. * * @param capacity the capacity */ public ByteArrayBuffer(final int capacity) { super(); Args.notNegative(capacity, "Buffer capacity"); this.array = new byte[capacity]; } private void expand(final int newlen) { final byte[] newArray = new byte[Math.max(this.array.length << 1, newlen)]; System.arraycopy(this.array, 0, newArray, 0, this.len); this.array = newArray; } /** * Appends {@code len} bytes to this buffer from the given source * array starting at index {@code off}. The capacity of the buffer * is increased, if necessary, to accommodate all {@code len} bytes. * * @param b the bytes to be appended. * @param off the index of the first byte to append. * @param len the number of bytes to append. * @throws IndexOutOfBoundsException if {@code off} if out of * range, {@code len} is negative, or * {@code off} + {@code len} is out of range. */ public void append(final byte[] b, final int off, final int len) { if (b == null) { return; } if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) < 0) || ((off + len) > b.length)) { throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); } if (len == 0) { return; } final int newlen = this.len + len; if (newlen > this.array.length) { expand(newlen); } System.arraycopy(b, off, this.array, this.len, len); this.len = newlen; } /** * Appends {@code b} byte to this buffer. The capacity of the buffer * is increased, if necessary, to accommodate the additional byte. * * @param b the byte to be appended. */ public void append(final int b) { final int newlen = this.len + 1; if (newlen > this.array.length) { expand(newlen); } this.array[this.len] = (byte)b; this.len = newlen; } /** * Appends {@code len} chars to this buffer from the given source * array starting at index {@code off}. The capacity of the buffer * is increased if necessary to accommodate all {@code len} chars. *

* The chars are converted to bytes using simple cast. * * @param b the chars to be appended. * @param off the index of the first char to append. * @param len the number of bytes to append. * @throws IndexOutOfBoundsException if {@code off} if out of * range, {@code len} is negative, or * {@code off} + {@code len} is out of range. */ public void append(final char[] b, final int off, final int len) { if (b == null) { return; } if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) < 0) || ((off + len) > b.length)) { throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); } if (len == 0) { return; } final int oldlen = this.len; final int newlen = oldlen + len; if (newlen > this.array.length) { expand(newlen); } for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) { final int c = b[i1]; if ((c >= 0x20 && c <= 0x7E) || // Visible ASCII (c >= 0xA0 && c <= 0xFF) || // Visible ISO-8859-1 c == 0x09) { // TAB this.array[i2] = (byte) c; } else { this.array[i2] = '?'; } } this.len = newlen; } /** * Appends {@code len} chars to this buffer from the given source * char array buffer starting at index {@code off}. The capacity * of the buffer is increased if necessary to accommodate all * {@code len} chars. *

* The chars are converted to bytes using simple cast. * * @param b the chars to be appended. * @param off the index of the first char to append. * @param len the number of bytes to append. * @throws IndexOutOfBoundsException if {@code off} if out of * range, {@code len} is negative, or * {@code off} + {@code len} is out of range. */ public void append(final CharArrayBuffer b, final int off, final int len) { if (b == null) { return; } append(b.array(), off, len); } public void append(final ByteBuffer buffer) { if (buffer == null) { return; } final int bufferLength = buffer.remaining(); if (bufferLength > 0) { final int newLength = this.len + bufferLength; if (newLength > this.array.length) { expand(newLength); } buffer.get(this.array, this.len, bufferLength); this.len = newLength; } } /** * Clears content of the buffer. The underlying byte array is not resized. */ public void clear() { this.len = 0; } /** * Converts the content of this buffer to an array of bytes. * * @return byte array */ public byte[] toByteArray() { final byte[] b = new byte[this.len]; if (this.len > 0) { System.arraycopy(this.array, 0, b, 0, this.len); } return b; } /** * Returns the {@code byte} value in this buffer at the specified * index. The index argument must be greater than or equal to * {@code 0}, and less than the length of this buffer. * * @param i the index of the desired byte value. * @return the byte value at the specified index. * @throws IndexOutOfBoundsException if {@code index} is * negative or greater than or equal to {@link #length()}. */ public int byteAt(final int i) { return this.array[i]; } /** * Returns the current capacity. The capacity is the amount of storage * available for newly appended bytes, beyond which an allocation * will occur. * * @return the current capacity */ public int capacity() { return this.array.length; } /** * Returns the length of the buffer (byte count). * * @return the length of the buffer */ public int length() { return this.len; } /** * Ensures that the capacity is at least equal to the specified minimum. * If the current capacity is less than the argument, then a new internal * array is allocated with greater capacity. If the {@code required} * argument is non-positive, this method takes no action. * * @param required the minimum required capacity. * * @since 4.1 */ public void ensureCapacity(final int required) { if (required <= 0) { return; } final int available = this.array.length - this.len; if (required > available) { expand(this.len + required); } } /** * Returns reference to the underlying byte array. * * @return the byte array. */ public byte[] array() { return this.array; } /** * Sets the length of the buffer. The new length value is expected to be * less than the current capacity and greater than or equal to * {@code 0}. * * @param len the new length * @throws IndexOutOfBoundsException if the * {@code len} argument is greater than the current * capacity of the buffer or less than {@code 0}. */ public void setLength(final int len) { if (len < 0 || len > this.array.length) { throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.array.length); } this.len = len; } /** * Returns {@code true} if this buffer is empty, that is, its * {@link #length()} is equal to {@code 0}. * @return {@code true} if this buffer is empty, {@code false} * otherwise. */ public boolean isEmpty() { return this.len == 0; } /** * Returns {@code true} if this buffer is full, that is, its * {@link #length()} is equal to its {@link #capacity()}. * @return {@code true} if this buffer is full, {@code false} * otherwise. */ public boolean isFull() { return this.len == this.array.length; } /** * Returns the index within this buffer of the first occurrence of the * specified byte, starting the search at the specified * {@code beginIndex} and finishing at {@code endIndex}. * If no such byte occurs in this buffer within the specified bounds, * {@code -1} is returned. *

* There is no restriction on the value of {@code beginIndex} and * {@code endIndex}. If {@code beginIndex} is negative, * it has the same effect as if it were zero. If {@code endIndex} is * greater than {@link #length()}, it has the same effect as if it were * {@link #length()}. If the {@code beginIndex} is greater than * the {@code endIndex}, {@code -1} is returned. * * @param b the byte to search for. * @param from the index to start the search from. * @param to the index to finish the search at. * @return the index of the first occurrence of the byte in the buffer * within the given bounds, or {@code -1} if the byte does * not occur. * * @since 4.1 */ public int indexOf(final byte b, final int from, final int to) { int beginIndex = from; if (beginIndex < 0) { beginIndex = 0; } int endIndex = to; if (endIndex > this.len) { endIndex = this.len; } if (beginIndex > endIndex) { return -1; } for (int i = beginIndex; i < endIndex; i++) { if (this.array[i] == b) { return i; } } return -1; } /** * Returns the index within this buffer of the first occurrence of the * specified byte, starting the search at {@code 0} and finishing * at {@link #length()}. If no such byte occurs in this buffer within * those bounds, {@code -1} is returned. * * @param b the byte to search for. * @return the index of the first occurrence of the byte in the * buffer, or {@code -1} if the byte does not occur. * * @since 4.1 */ public int indexOf(final byte b) { return indexOf(b, 0, this.len); } } httpcore5/src/main/java/org/apache/hc/core5/util/TimeValue.java0100664 0000000 0000000 00000033614 14403631147 023324 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.text.ParseException; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.Locale; import java.util.Objects; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Represents a time value as a {@code long} time and a {@link TimeUnit}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class TimeValue implements Comparable { static final int INT_UNDEFINED = -1; /** * A constant holding the maximum value a {@code TimeValue} can have: {@code Long.MAX_VALUE} days. */ public static final TimeValue MAX_VALUE = ofDays(Long.MAX_VALUE); /** * A negative one millisecond {@link TimeValue}. */ public static final TimeValue NEG_ONE_MILLISECOND = TimeValue.of(INT_UNDEFINED, TimeUnit.MILLISECONDS); /** * A negative one second {@link TimeValue}. */ public static final TimeValue NEG_ONE_SECOND = TimeValue.of(INT_UNDEFINED, TimeUnit.SECONDS); /** * A zero milliseconds {@link TimeValue}. */ public static final TimeValue ZERO_MILLISECONDS = TimeValue.of(0, TimeUnit.MILLISECONDS); /** * Returns the given {@code long} value as an {@code int} where long values out of int range are returned as * {@link Integer#MIN_VALUE} and {@link Integer#MAX_VALUE}. * *

* For example: {@code TimeValue.asBoundInt(Long.MAX_VALUE)} returns {@code Integer.MAX_VALUE}. *

* * @param value a long value to convert * @return an int value bound within {@link Integer#MIN_VALUE} and {@link Integer#MAX_VALUE}. */ public static int asBoundInt(final long value) { if (value > Integer.MAX_VALUE) { return Integer.MAX_VALUE; } else if (value < Integer.MIN_VALUE) { return Integer.MIN_VALUE; } return (int) value; } /** * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns the given * {@code defaultValue}. * * @param timeValue may be {@code null} * @param defaultValue may be {@code null} * @return {@code timeValue} or {@code defaultValue} */ public static T defaultsTo(final T timeValue, final T defaultValue) { return timeValue != null ? timeValue : defaultValue; } /** * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns * {@link #NEG_ONE_SECOND}. * * @param timeValue may be {@code null} * @return {@code timeValue} or {@link #NEG_ONE_SECOND} */ public static TimeValue defaultsToNegativeOneMillisecond(final TimeValue timeValue) { return defaultsTo(timeValue, NEG_ONE_MILLISECOND); } /** * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns * {@link #NEG_ONE_SECOND}. * * @param timeValue may be {@code null} * @return {@code timeValue} or {@link #NEG_ONE_SECOND} */ public static TimeValue defaultsToNegativeOneSecond(final TimeValue timeValue) { return defaultsTo(timeValue, NEG_ONE_SECOND); } /** * Returns the given {@code timeValue} if it is not {@code null}, if {@code null} then returns * {@link #ZERO_MILLISECONDS}. * * @param timeValue may be {@code null} * @return {@code timeValue} or {@link #ZERO_MILLISECONDS} */ public static TimeValue defaultsToZeroMilliseconds(final TimeValue timeValue) { return defaultsTo(timeValue, ZERO_MILLISECONDS); } public static boolean isNonNegative(final TimeValue timeValue) { return timeValue != null && timeValue.getDuration() >= 0; } public static boolean isPositive(final TimeValue timeValue) { return timeValue != null && timeValue.getDuration() > 0; } /** * Creates a TimeValue. * * @param duration the time duration in the given {@code timeUnit}. * @param timeUnit the time unit for the given duration. * @return a Timeout. */ public static TimeValue of(final long duration, final TimeUnit timeUnit) { return new TimeValue(duration, timeUnit); } /** * Creates a TimeValue from a Duration. * * @param duration the time duration. * @return a Timeout * @since 5.2 */ public static TimeValue of(final Duration duration) { final long seconds = duration.getSeconds(); final long nanoOfSecond = duration.getNano(); if (seconds == 0) { // no conversion return of(nanoOfSecond, TimeUnit.NANOSECONDS); } else if (nanoOfSecond == 0) { // no conversion return of(seconds, TimeUnit.SECONDS); } // conversion attempts try { return of(duration.toNanos(), TimeUnit.NANOSECONDS); } catch (final ArithmeticException e) { try { return of(duration.toMillis(), TimeUnit.MILLISECONDS); } catch (final ArithmeticException e1) { // backstop return of(seconds, TimeUnit.SECONDS); } } } public static TimeValue ofDays(final long days) { return of(days, TimeUnit.DAYS); } public static TimeValue ofHours(final long hours) { return of(hours, TimeUnit.HOURS); } public static TimeValue ofMicroseconds(final long microseconds) { return of(microseconds, TimeUnit.MICROSECONDS); } public static TimeValue ofMilliseconds(final long millis) { return of(millis, TimeUnit.MILLISECONDS); } public static TimeValue ofMinutes(final long minutes) { return of(minutes, TimeUnit.MINUTES); } public static TimeValue ofNanoseconds(final long nanoseconds) { return of(nanoseconds, TimeUnit.NANOSECONDS); } public static TimeValue ofSeconds(final long seconds) { return of(seconds, TimeUnit.SECONDS); } /** * Converts a {@link TimeUnit} to the equivalent {@link ChronoUnit}. * * @return the converted equivalent ChronoUnit */ static ChronoUnit toChronoUnit(final TimeUnit timeUnit) { switch (Objects.requireNonNull(timeUnit)) { case NANOSECONDS: return ChronoUnit.NANOS; case MICROSECONDS: return ChronoUnit.MICROS; case MILLISECONDS: return ChronoUnit.MILLIS; case SECONDS: return ChronoUnit.SECONDS; case MINUTES: return ChronoUnit.MINUTES; case HOURS: return ChronoUnit.HOURS; case DAYS: return ChronoUnit.DAYS; default: throw new IllegalArgumentException(timeUnit.toString()); } } /** * Parses a TimeValue in the format {@code }, for example {@code "1200 MILLISECONDS"}. *

* Parses: *

*
    *
  • {@code "1200 MILLISECONDS"}.
  • *
  • {@code " 1200 MILLISECONDS "}, spaces are ignored.
  • *
  • {@code "1 MINUTE"}, singular units.
  • *
  • *
* * * @param value the TimeValue to parse * @return a new TimeValue * @throws ParseException if the number cannot be parsed */ public static TimeValue parse(final String value) throws ParseException { final String split[] = value.trim().split("\\s+"); if (split.length < 2) { throw new IllegalArgumentException( String.format("Expected format for : %s", value)); } final String clean0 = split[0].trim(); final String clean1 = split[1].trim().toUpperCase(Locale.ROOT); final String timeUnitStr = clean1.endsWith("S") ? clean1 : clean1 + "S"; return TimeValue.of(Long.parseLong(clean0), TimeUnit.valueOf(timeUnitStr)); } private final long duration; private final TimeUnit timeUnit; TimeValue(final long duration, final TimeUnit timeUnit) { super(); this.duration = duration; this.timeUnit = Args.notNull(timeUnit, "timeUnit"); } public long convert(final TimeUnit targetTimeUnit) { Args.notNull(targetTimeUnit, "timeUnit"); return targetTimeUnit.convert(duration, timeUnit); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof TimeValue) { final TimeValue that = (TimeValue) obj; final long thisDuration = this.convert(TimeUnit.NANOSECONDS); final long thatDuration = that.convert(TimeUnit.NANOSECONDS); return thisDuration == thatDuration; } return false; } /** * Returns a TimeValue whose value is {@code (this / divisor)}. * * @param divisor * value by which this TimeValue is to be divided. * @return {@code this / divisor} * @throws ArithmeticException * if {@code divisor} is zero. */ public TimeValue divide(final long divisor) { final long newDuration = duration / divisor; return of(newDuration, timeUnit); } /** * Returns a TimeValue whose value is {@code (this / divisor)}. * * @param divisor * value by which this TimeValue is to be divided. * @param targetTimeUnit * the target TimeUnit * @return {@code this / divisor} * @throws ArithmeticException * if {@code divisor} is zero. */ public TimeValue divide(final long divisor, final TimeUnit targetTimeUnit) { return of(convert(targetTimeUnit) / divisor, targetTimeUnit); } public long getDuration() { return duration; } public TimeUnit getTimeUnit() { return timeUnit; } @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, this.convert(TimeUnit.NANOSECONDS)); return hash; } public TimeValue min(final TimeValue other) { return this.compareTo(other) > 0 ? other : this; } private TimeUnit min(final TimeUnit other) { return scale() > scale(other) ? other : getTimeUnit(); } private int scale() { return scale(timeUnit); } /** * Returns a made up scale for TimeUnits. * * @param tUnit * a TimeUnit * @return a number from 1 to 7, where 1 is NANOSECONDS and 7 DAYS. */ private int scale(final TimeUnit tUnit) { switch (tUnit) { case NANOSECONDS: return 1; case MICROSECONDS: return 2; case MILLISECONDS: return 3; case SECONDS: return 4; case MINUTES: return 5; case HOURS: return 6; case DAYS: return 7; default: // Should never happens unless Java adds to the enum. throw new IllegalStateException(); } } public void sleep() throws InterruptedException { timeUnit.sleep(duration); } public void timedJoin(final Thread thread) throws InterruptedException { timeUnit.timedJoin(thread, duration); } public void timedWait(final Object obj) throws InterruptedException { timeUnit.timedWait(obj, duration); } public long toDays() { return timeUnit.toDays(duration); } /** * Converts this instance of to a Duration. * * @return a Duration. * @since 5.2 */ public Duration toDuration() { return duration == 0 ? Duration.ZERO : Duration.of(duration, toChronoUnit(timeUnit)); } public long toHours() { return timeUnit.toHours(duration); } public long toMicroseconds() { return timeUnit.toMicros(duration); } public long toMilliseconds() { return timeUnit.toMillis(duration); } public int toMillisecondsIntBound() { return asBoundInt(toMilliseconds()); } public long toMinutes() { return timeUnit.toMinutes(duration); } public long toNanoseconds() { return timeUnit.toNanos(duration); } public long toSeconds() { return timeUnit.toSeconds(duration); } public int toSecondsIntBound() { return asBoundInt(toSeconds()); } @Override public int compareTo(final TimeValue other) { final TimeUnit targetTimeUnit = min(other.getTimeUnit()); return Long.compare(convert(targetTimeUnit), other.convert(targetTimeUnit)); } @Override public String toString() { return String.format("%d %s", duration, timeUnit); } public Timeout toTimeout() { return Timeout.of(duration, timeUnit); } } httpcore5/src/main/java/org/apache/hc/core5/util/Args.java0100664 0000000 0000000 00000020556 14245617503 022332 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.lang.reflect.Array; import java.util.Collection; import java.util.Map; import java.util.Objects; import org.apache.hc.core5.http.EntityDetails; public class Args { public static void check(final boolean expression, final String message) { if (!expression) { throw new IllegalArgumentException(message); } } public static void check(final boolean expression, final String message, final Object... args) { if (!expression) { throw new IllegalArgumentException(String.format(message, args)); } } public static void check(final boolean expression, final String message, final Object arg) { if (!expression) { throw new IllegalArgumentException(String.format(message, arg)); } } public static long checkContentLength(final EntityDetails entityDetails) { // -1 is a special value, // 0 is allowed as well, // but never more than Integer.MAX_VALUE. return checkRange(entityDetails.getContentLength(), -1, Integer.MAX_VALUE, "HTTP entity too large to be buffered in memory)"); } public static int checkRange(final int value, final int lowInclusive, final int highInclusive, final String message) { if (value < lowInclusive || value > highInclusive) { throw illegalArgumentException("%s: %d is out of range [%d, %d]", message, value, lowInclusive, highInclusive); } return value; } public static long checkRange(final long value, final long lowInclusive, final long highInclusive, final String message) { if (value < lowInclusive || value > highInclusive) { throw illegalArgumentException("%s: %d is out of range [%d, %d]", message, value, lowInclusive, highInclusive); } return value; } public static T containsNoBlanks(final T argument, final String name) { notNull(argument, name); if (isEmpty(argument)) { throw illegalArgumentExceptionNotEmpty(name); } if (TextUtils.containsBlanks(argument)) { throw new IllegalArgumentException(name + " must not contain blanks"); } return argument; } private static IllegalArgumentException illegalArgumentException(final String format, final Object... args) { return new IllegalArgumentException(String.format(format, args)); } private static IllegalArgumentException illegalArgumentExceptionNotEmpty(final String name) { return new IllegalArgumentException(name + " must not be empty"); } private static NullPointerException NullPointerException(final String name) { return new NullPointerException(name + " must not be null"); } public static T notBlank(final T argument, final String name) { notNull(argument, name); if (TextUtils.isBlank(argument)) { throw new IllegalArgumentException(name + " must not be blank"); } return argument; } public static T notEmpty(final T argument, final String name) { notNull(argument, name); if (isEmpty(argument)) { throw illegalArgumentExceptionNotEmpty(name); } return argument; } public static > T notEmpty(final T argument, final String name) { notNull(argument, name); if (isEmpty(argument)) { throw illegalArgumentExceptionNotEmpty(name); } return argument; } public static T notEmpty(final T argument, final String name) { notNull(argument, name); if (isEmpty(argument)) { throw illegalArgumentExceptionNotEmpty(name); } return argument; } public static int notNegative(final int n, final String name) { if (n < 0) { throw illegalArgumentException("%s must not be negative: %d", name, n); } return n; } public static long notNegative(final long n, final String name) { if (n < 0) { throw illegalArgumentException("%s must not be negative: %d", name, n); } return n; } /** *

Validate that the specified argument is not {@code null}; * otherwise throwing an exception with the specified message. * *

Args.notNull(myObject, "The object must not be null");
* * @param the object type * @param argument the object to check * @param name the {@link String} exception message if invalid, not null * @return the validated object (never {@code null} for method chaining) * @throws NullPointerException if the object is {@code null} */ public static T notNull(final T argument, final String name) { return Objects.requireNonNull(argument, name); } /** *

Checks if an Object is empty or null.

* * The following types are supported: *
    *
  • {@link CharSequence}: Considered empty if its length is zero.
  • *
  • {@code Array}: Considered empty if its length is zero.
  • *
  • {@link Collection}: Considered empty if it has zero elements.
  • *
  • {@link Map}: Considered empty if it has zero key-value mappings.
  • *
* *
     * Args.isEmpty(null)             = true
     * Args.isEmpty("")               = true
     * Args.isEmpty("ab")             = false
     * Args.isEmpty(new int[]{})      = true
     * Args.isEmpty(new int[]{1,2,3}) = false
     * Args.isEmpty(1234)             = false
     * 
* * @param object the {@code Object} to test, may be {@code null} * @return {@code true} if the object has a supported type and is empty or null, * {@code false} otherwise * @since 5.1 */ public static boolean isEmpty(final Object object) { if (object == null) { return true; } if (object instanceof CharSequence) { return ((CharSequence) object).length() == 0; } if (object.getClass().isArray()) { return Array.getLength(object) == 0; } if (object instanceof Collection) { return ((Collection) object).isEmpty(); } if (object instanceof Map) { return ((Map) object).isEmpty(); } return false; } public static int positive(final int n, final String name) { if (n <= 0) { throw illegalArgumentException("%s must not be negative or zero: %d", name, n); } return n; } public static long positive(final long n, final String name) { if (n <= 0) { throw illegalArgumentException("%s must not be negative or zero: %d", name, n); } return n; } public static T positive(final T timeValue, final String name) { positive(timeValue.getDuration(), name); return timeValue; } /** * Private constructor so that no instances can be created. This class * contains only static utility methods. */ private Args() { } } httpcore5/src/main/java/org/apache/hc/core5/util/TimeoutValueException.java0100664 0000000 0000000 00000006404 14245617503 025734 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.util.concurrent.TimeoutException; /** * A specialization of {@link TimeoutException} that carries a {@link Timeout} deadline and the actual value. * * @since 5.0 */ public class TimeoutValueException extends TimeoutException { private static final long serialVersionUID = 1L; /** * Creates a new exception for the given timeout deadline and actual timeout. * * @param timeoutDeadline How long was the expected timeout in milliseconds. * @param timeoutActual How long we actually waited in milliseconds. * @return a new TimeoutValueException. */ public static TimeoutValueException fromMilliseconds(final long timeoutDeadline, final long timeoutActual) { return new TimeoutValueException(Timeout.ofMilliseconds(min0(timeoutDeadline)), Timeout.ofMilliseconds(min0(timeoutActual))); } /** * Returns the given {@code value} if positive, otherwise returns 0. * * @param value any timeout * @return the given {@code value} if positive, otherwise returns 0. */ private static long min0(final long value) { return value < 0 ? 0 : value; } private final Timeout actual; private final Timeout deadline; /** * Creates a new exception for the given timeout deadline and actual timeout. * * @param deadline How long was the expected timeout. * @param actual How long we actually waited. */ public TimeoutValueException(final Timeout deadline, final Timeout actual) { super(String.format("Timeout deadline: %s, actual: %s", deadline, actual)); this.actual = actual; this.deadline = deadline; } /** * Gets how long was the expected timeout in milliseconds. * * @return how long was the expected timeout in milliseconds. */ public Timeout getActual() { return actual; } /** * Gets how long we actually waited in milliseconds. * * @return how long we actually waited in milliseconds. */ public Timeout getDeadline() { return deadline; } } httpcore5/src/main/java/org/apache/hc/core5/util/Identifiable.java0100664 0000000 0000000 00000002471 14245617503 024011 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; /** * Object with a unique identifier. * * @since 5.0 */ public interface Identifiable { String getId(); } httpcore5/src/main/java/org/apache/hc/core5/util/VersionInfo.java0100664 0000000 0000000 00000026420 14403631147 023667 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; /** * Provides access to version information for HTTP components. * Static methods are used to extract version information from property * files that are automatically packaged with HTTP component release JARs. *

* All available version information is provided in strings, where * the string format is informal and subject to change without notice. * Version information is provided for debugging output and interpretation * by humans, not for automated processing in applications. *

* * @since 4.0 */ public class VersionInfo { /** A string constant for unavailable information. */ public final static String UNAVAILABLE = "UNAVAILABLE"; /** The filename of the version information files. */ public final static String VERSION_PROPERTY_FILE = "version.properties"; // the property names public final static String PROPERTY_MODULE = "info.module"; public final static String PROPERTY_RELEASE = "info.release"; /** * @deprecated This will be removed in 6.0. */ @Deprecated public final static String PROPERTY_TIMESTAMP = "info.timestamp"; /** The package that contains the version information. */ private final String infoPackage; /** The module from the version info. */ private final String infoModule; /** The release from the version info. */ private final String infoRelease; /** The timestamp from the version info. */ private final String infoTimestamp; /** The classloader from which the version info was obtained. */ private final String infoClassloader; /** * An empty immutable {@code VersionInfo} array. */ private static final VersionInfo[] EMPTY_VERSION_INFO_ARRAY = {}; /** * Instantiates version information. * * @param pckg the package * @param module the module, or {@code null} * @param release the release, or {@code null} * @param time the build time, or {@code null} * @param clsldr the class loader, or {@code null} */ protected VersionInfo(final String pckg, final String module, final String release, final String time, final String clsldr) { Args.notNull(pckg, "Package identifier"); infoPackage = pckg; infoModule = (module != null) ? module : UNAVAILABLE; infoRelease = (release != null) ? release : UNAVAILABLE; infoTimestamp = (time != null) ? time : UNAVAILABLE; infoClassloader = (clsldr != null) ? clsldr : UNAVAILABLE; } /** * Obtains the package name. * The package name identifies the module or informal unit. * * @return the package name, never {@code null} */ public final String getPackage() { return infoPackage; } /** * Obtains the name of the versioned module or informal unit. * This data is read from the version information for the package. * * @return the module name, never {@code null} */ public final String getModule() { return infoModule; } /** * Obtains the release of the versioned module or informal unit. * This data is read from the version information for the package. * * @return the release version, never {@code null} */ public final String getRelease() { return infoRelease; } /** * Obtains the timestamp of the versioned module or informal unit. * This data is read from the version information for the package. * * @return the timestamp, never {@code null} * @deprecated This will be removed in 6.0. */ @Deprecated public final String getTimestamp() { return infoTimestamp; } /** * Obtains the classloader used to read the version information. * This is just the {@code toString} output of the classloader, * since the version information should not keep a reference to * the classloader itself. That could prevent garbage collection. * * @return the classloader description, never {@code null} */ public final String getClassloader() { return infoClassloader; } /** * Provides the version information in human-readable format. * * @return a string holding this version information */ @Override public String toString() { final StringBuilder sb = new StringBuilder (20 + infoPackage.length() + infoModule.length() + infoRelease.length() + infoTimestamp.length() + infoClassloader.length()); sb.append("VersionInfo(") .append(infoPackage).append(':').append(infoModule); // If version info is missing, a single "UNAVAILABLE" for the module // is sufficient. Everything else just clutters the output. if (!UNAVAILABLE.equals(infoRelease)) { sb.append(':').append(infoRelease); } sb.append(')'); if (!UNAVAILABLE.equals(infoClassloader)) { sb.append('@').append(infoClassloader); } return sb.toString(); } /** * Loads version information for a list of packages. * * @param pckgs the packages for which to load version info * @param clsldr the classloader to load from, or * {@code null} for the thread context classloader * * @return the version information for all packages found, * never {@code null} */ public static VersionInfo[] loadVersionInfo(final String[] pckgs, final ClassLoader clsldr) { Args.notNull(pckgs, "Package identifier array"); final List vil = new ArrayList<>(pckgs.length); for (final String pckg : pckgs) { final VersionInfo vi = loadVersionInfo(pckg, clsldr); if (vi != null) { vil.add(vi); } } return vil.toArray(EMPTY_VERSION_INFO_ARRAY); } /** * Loads version information for a package. * * @param pckg the package for which to load version information, * for example "org.apache.http". * The package name should NOT end with a dot. * @param clsldr the classloader to load from, or * {@code null} for the thread context classloader * * @return the version information for the argument package, or * {@code null} if not available */ public static VersionInfo loadVersionInfo(final String pckg, final ClassLoader clsldr) { Args.notNull(pckg, "Package identifier"); final ClassLoader cl = clsldr != null ? clsldr : Thread.currentThread().getContextClassLoader(); Properties vip = null; // version info properties, if available try { // org.apache.http becomes // org/apache/http/version.properties try (final InputStream is = cl.getResourceAsStream(pckg.replace('.', '/') + "/" + VERSION_PROPERTY_FILE)) { if (is != null) { final Properties props = new Properties(); props.load(is); vip = props; } } } catch (final IOException ex) { // shamelessly munch this exception } VersionInfo result = null; if (vip != null) { result = fromMap(pckg, vip, cl); } return result; } /** * Instantiates version information from properties. * * @param pckg the package for the version information * @param info the map from string keys to string values, * for example {@link java.util.Properties} * @param clsldr the classloader, or {@code null} * * @return the version information */ protected static VersionInfo fromMap(final String pckg, final Map info, final ClassLoader clsldr) { Args.notNull(pckg, "Package identifier"); String module = null; String release = null; if (info != null) { module = (String) info.get(PROPERTY_MODULE); if ((module != null) && (module.length() < 1)) { module = null; } release = (String) info.get(PROPERTY_RELEASE); if ((release != null) && ((release.length() < 1) || (release.equals("${project.version}")))) { release = null; } } // if info String clsldrstr = null; if (clsldr != null) { clsldrstr = clsldr.toString(); } return new VersionInfo(pckg, module, release, null, clsldrstr); } /** * Gets software information as {@code "/ (Java/)"}. If release is * {@link #UNAVAILABLE}, it will be omitted. *

* For example: *

"Apache-HttpClient/4.3 (Java/1.6.0_35)"
* * @param name the component name, like "Apache-HttpClient". * @param pkg * the package for which to load version information, for example "org.apache.http". The package name * should NOT end with a dot. * @param cls * the class' class loader to load from, or {@code null} for the thread context class loader * @since 4.3 */ public static String getSoftwareInfo(final String name, final String pkg, final Class cls) { // determine the release version from packaged version info final VersionInfo vi = VersionInfo.loadVersionInfo(pkg, cls.getClassLoader()); final String release = (vi != null) ? vi.getRelease() : VersionInfo.UNAVAILABLE; final String javaVersion = System.getProperty("java.version"); String nameAndRelease = name; if (!UNAVAILABLE.equals(release)) { nameAndRelease += "/" + release; } return String.format("%s (Java/%s)", nameAndRelease, javaVersion); } } httpcore5/src/main/java/org/apache/hc/core5/util/ReflectionUtils.java0100664 0000000 0000000 00000006013 14403631147 024535 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; import org.apache.hc.core5.annotation.Internal; @Internal public final class ReflectionUtils { public static void callSetter(final Object object, final String setterName, final Class type, final Object value) { try { final Class clazz = object.getClass(); final Method method = clazz.getMethod("set" + setterName, type); setAccessible(method); method.invoke(object, value); } catch (final Exception ignore) { } } public static T callGetter(final Object object, final String getterName, final Class resultType) { try { final Class clazz = object.getClass(); final Method method = clazz.getMethod("get" + getterName); setAccessible(method); return resultType.cast(method.invoke(object)); } catch (final Exception ignore) { return null; } } private static void setAccessible(final Method method) { AccessController.doPrivileged((PrivilegedAction) () -> { method.setAccessible(true); return null; }); } public static int determineJRELevel() { final String s = System.getProperty("java.version"); final String[] parts = s.split("\\."); if (parts.length > 0) { try { final int majorVersion = Integer.parseInt(parts[0]); if (majorVersion > 1) { return majorVersion; } else if (majorVersion == 1 && parts.length > 1) { return Integer.parseInt(parts[1]); } } catch (final NumberFormatException ignore) { } } return 7; } } httpcore5/src/main/java/org/apache/hc/core5/util/CharArrayBuffer.java0100664 0000000 0000000 00000042267 14245617503 024447 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.io.Serializable; import java.nio.CharBuffer; import org.apache.hc.core5.http.Chars; /** * A resizable char array. * * @since 4.0 */ public final class CharArrayBuffer implements CharSequence, Serializable { private static final long serialVersionUID = -6208952725094867135L; private char[] array; private int len; /** * Creates an instance of {@link CharArrayBuffer} with the given initial * capacity. * * @param capacity the capacity */ public CharArrayBuffer(final int capacity) { super(); Args.notNegative(capacity, "Buffer capacity"); this.array = new char[capacity]; } private void expand(final int newlen) { final char[] newArray = new char[Math.max(this.array.length << 1, newlen)]; System.arraycopy(this.array, 0, newArray, 0, this.len); this.array = newArray; } /** * Appends {@code len} chars to this buffer from the given source * array starting at index {@code off}. The capacity of the buffer * is increased, if necessary, to accommodate all {@code len} chars. * * @param b the chars to be appended. * @param off the index of the first char to append. * @param len the number of chars to append. * @throws IndexOutOfBoundsException if {@code off} is out of * range, {@code len} is negative, or * {@code off} + {@code len} is out of range. */ public void append(final char[] b, final int off, final int len) { if (b == null) { return; } if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) < 0) || ((off + len) > b.length)) { throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); } if (len == 0) { return; } final int newlen = this.len + len; if (newlen > this.array.length) { expand(newlen); } System.arraycopy(b, off, this.array, this.len, len); this.len = newlen; } /** * Appends chars of the given string to this buffer. The capacity of the * buffer is increased, if necessary, to accommodate all chars. * * @param str the string. */ public void append(final String str) { final String s = str != null ? str : "null"; final int strlen = s.length(); final int newlen = this.len + strlen; if (newlen > this.array.length) { expand(newlen); } s.getChars(0, strlen, this.array, this.len); this.len = newlen; } /** * Appends {@code len} chars to this buffer from the given source * buffer starting at index {@code off}. The capacity of the * destination buffer is increased, if necessary, to accommodate all * {@code len} chars. * * @param b the source buffer to be appended. * @param off the index of the first char to append. * @param len the number of chars to append. * @throws IndexOutOfBoundsException if {@code off} is out of * range, {@code len} is negative, or * {@code off} + {@code len} is out of range. */ public void append(final CharArrayBuffer b, final int off, final int len) { if (b == null) { return; } append(b.array, off, len); } /** * Appends all chars to this buffer from the given source buffer starting * at index {@code 0}. The capacity of the destination buffer is * increased, if necessary, to accommodate all {@link #length()} chars. * * @param b the source buffer to be appended. */ public void append(final CharArrayBuffer b) { if (b == null) { return; } append(b.array,0, b.len); } /** * Appends {@code ch} char to this buffer. The capacity of the buffer * is increased, if necessary, to accommodate the additional char. * * @param ch the char to be appended. */ public void append(final char ch) { final int newlen = this.len + 1; if (newlen > this.array.length) { expand(newlen); } this.array[this.len] = ch; this.len = newlen; } /** * Appends {@code len} bytes to this buffer from the given source * array starting at index {@code off}. The capacity of the buffer * is increased, if necessary, to accommodate all {@code len} bytes. *

* The bytes are converted to chars using simple cast. * * @param b the bytes to be appended. * @param off the index of the first byte to append. * @param len the number of bytes to append. * @throws IndexOutOfBoundsException if {@code off} is out of * range, {@code len} is negative, or * {@code off} + {@code len} is out of range. */ public void append(final byte[] b, final int off, final int len) { if (b == null) { return; } if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) < 0) || ((off + len) > b.length)) { throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); } if (len == 0) { return; } final int oldlen = this.len; final int newlen = oldlen + len; if (newlen > this.array.length) { expand(newlen); } for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) { this.array[i2] = (char) (b[i1] & 0xff); } this.len = newlen; } /** * Appends {@code len} bytes to this buffer from the given source * array starting at index {@code off}. The capacity of the buffer * is increased, if necessary, to accommodate all {@code len} bytes. *

* The bytes are converted to chars using simple cast. * * @param b the bytes to be appended. * @param off the index of the first byte to append. * @param len the number of bytes to append. * @throws IndexOutOfBoundsException if {@code off} is out of * range, {@code len} is negative, or * {@code off} + {@code len} is out of range. */ public void append(final ByteArrayBuffer b, final int off, final int len) { if (b == null) { return; } append(b.array(), off, len); } /** * Appends chars of the textual representation of the given object to this * buffer. The capacity of the buffer is increased, if necessary, to * accommodate all chars. * * @param obj the object. */ public void append(final Object obj) { append(String.valueOf(obj)); } /** * Clears content of the buffer. The underlying char array is not resized. */ public void clear() { this.len = 0; } /** * Converts the content of this buffer to an array of chars. * * @return char array */ public char[] toCharArray() { final char[] b = new char[this.len]; if (this.len > 0) { System.arraycopy(this.array, 0, b, 0, this.len); } return b; } /** * Returns the {@code char} value in this buffer at the specified * index. The index argument must be greater than or equal to * {@code 0}, and less than the length of this buffer. * * @param i the index of the desired char value. * @return the char value at the specified index. * @throws IndexOutOfBoundsException if {@code index} is * negative or greater than or equal to {@link #length()}. */ @Override public char charAt(final int i) { return this.array[i]; } /** * Returns reference to the underlying char array. * * @return the char array. */ public char[] array() { return this.array; } /** * Returns the current capacity. The capacity is the amount of storage * available for newly appended chars, beyond which an allocation will * occur. * * @return the current capacity */ public int capacity() { return this.array.length; } /** * Returns the length of the buffer (char count). * * @return the length of the buffer */ @Override public int length() { return this.len; } /** * Ensures that the capacity is at least equal to the specified minimum. * If the current capacity is less than the argument, then a new internal * array is allocated with greater capacity. If the {@code required} * argument is non-positive, this method takes no action. * * @param required the minimum required capacity. */ public void ensureCapacity(final int required) { if (required <= 0) { return; } final int available = this.array.length - this.len; if (required > available) { expand(this.len + required); } } /** * Sets the length of the buffer. The new length value is expected to be * less than the current capacity and greater than or equal to * {@code 0}. * * @param len the new length * @throws IndexOutOfBoundsException if the * {@code len} argument is greater than the current * capacity of the buffer or less than {@code 0}. */ public void setLength(final int len) { if (len < 0 || len > this.array.length) { throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.array.length); } this.len = len; } /** * Returns {@code true} if this buffer is empty, that is, its * {@link #length()} is equal to {@code 0}. * @return {@code true} if this buffer is empty, {@code false} * otherwise. */ public boolean isEmpty() { return this.len == 0; } /** * Returns {@code true} if this buffer is full, that is, its * {@link #length()} is equal to its {@link #capacity()}. * @return {@code true} if this buffer is full, {@code false} * otherwise. */ public boolean isFull() { return this.len == this.array.length; } /** * Returns the index within this buffer of the first occurrence of the * specified character, starting the search at the specified * {@code beginIndex} and finishing at {@code endIndex}. * If no such character occurs in this buffer within the specified bounds, * {@code -1} is returned. *

* There is no restriction on the value of {@code beginIndex} and * {@code endIndex}. If {@code beginIndex} is negative, * it has the same effect as if it were zero. If {@code endIndex} is * greater than {@link #length()}, it has the same effect as if it were * {@link #length()}. If the {@code beginIndex} is greater than * the {@code endIndex}, {@code -1} is returned. * * @param ch the char to search for. * @param from the index to start the search from. * @param to the index to finish the search at. * @return the index of the first occurrence of the character in the buffer * within the given bounds, or {@code -1} if the character does * not occur. */ public int indexOf(final int ch, final int from, final int to) { int beginIndex = from; if (beginIndex < 0) { beginIndex = 0; } int endIndex = to; if (endIndex > this.len) { endIndex = this.len; } if (beginIndex > endIndex) { return -1; } for (int i = beginIndex; i < endIndex; i++) { if (this.array[i] == ch) { return i; } } return -1; } /** * Returns the index within this buffer of the first occurrence of the * specified character, starting the search at {@code 0} and finishing * at {@link #length()}. If no such character occurs in this buffer within * those bounds, {@code -1} is returned. * * @param ch the char to search for. * @return the index of the first occurrence of the character in the * buffer, or {@code -1} if the character does not occur. */ public int indexOf(final int ch) { return indexOf(ch, 0, this.len); } /** * Returns a substring of this buffer. The substring begins at the specified * {@code beginIndex} and extends to the character at index * {@code endIndex - 1}. * * @param beginIndex the beginning index, inclusive. * @param endIndex the ending index, exclusive. * @return the specified substring. * @throws StringIndexOutOfBoundsException if the * {@code beginIndex} is negative, or * {@code endIndex} is larger than the length of this * buffer, or {@code beginIndex} is larger than * {@code endIndex}. */ public String substring(final int beginIndex, final int endIndex) { if (beginIndex < 0) { throw new IndexOutOfBoundsException("Negative beginIndex: " + beginIndex); } if (endIndex > this.len) { throw new IndexOutOfBoundsException("endIndex: " + endIndex + " > length: " + this.len); } if (beginIndex > endIndex) { throw new IndexOutOfBoundsException("beginIndex: " + beginIndex + " > endIndex: " + endIndex); } return new String(this.array, beginIndex, endIndex - beginIndex); } private static boolean isWhitespace(final char ch) { return ch == Chars.SP || ch == Chars.HT || ch == Chars.CR || ch == Chars.LF; } /** * Returns a substring of this buffer with leading and trailing whitespace * omitted. The substring begins with the first non-whitespace character * from {@code beginIndex} and extends to the last * non-whitespace character with the index lesser than * {@code endIndex}. * * @param beginIndex the beginning index, inclusive. * @param endIndex the ending index, exclusive. * @return the specified substring. * @throws IndexOutOfBoundsException if the * {@code beginIndex} is negative, or * {@code endIndex} is larger than the length of this * buffer, or {@code beginIndex} is larger than * {@code endIndex}. */ public String substringTrimmed(final int beginIndex, final int endIndex) { if (beginIndex < 0) { throw new IndexOutOfBoundsException("Negative beginIndex: " + beginIndex); } if (endIndex > this.len) { throw new IndexOutOfBoundsException("endIndex: " + endIndex + " > length: " + this.len); } if (beginIndex > endIndex) { throw new IndexOutOfBoundsException("beginIndex: " + beginIndex + " > endIndex: " + endIndex); } int beginIndex0 = beginIndex; int endIndex0 = endIndex; while (beginIndex0 < endIndex && isWhitespace(this.array[beginIndex0])) { beginIndex0++; } while (endIndex0 > beginIndex0 && isWhitespace(this.array[endIndex0 - 1])) { endIndex0--; } return new String(this.array, beginIndex0, endIndex0 - beginIndex0); } /** * {@inheritDoc} * @since 4.4 */ @Override public CharSequence subSequence(final int beginIndex, final int endIndex) { if (beginIndex < 0) { throw new IndexOutOfBoundsException("Negative beginIndex: " + beginIndex); } if (endIndex > this.len) { throw new IndexOutOfBoundsException("endIndex: " + endIndex + " > length: " + this.len); } if (beginIndex > endIndex) { throw new IndexOutOfBoundsException("beginIndex: " + beginIndex + " > endIndex: " + endIndex); } return CharBuffer.wrap(this.array, beginIndex, endIndex - beginIndex); } @Override public String toString() { return new String(this.array, 0, this.len); } } httpcore5/src/main/java/org/apache/hc/core5/util/Asserts.java0100664 0000000 0000000 00000004725 14245617503 023062 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; public class Asserts { private Asserts() { // Do not allow utility class to be instantiated. } public static void check(final boolean expression, final String message) { if (!expression) { throw new IllegalStateException(message); } } public static void check(final boolean expression, final String message, final Object... args) { if (!expression) { throw new IllegalStateException(String.format(message, args)); } } public static void check(final boolean expression, final String message, final Object arg) { if (!expression) { throw new IllegalStateException(String.format(message, arg)); } } public static void notNull(final Object object, final String name) { if (object == null) { throw new IllegalStateException(name + " is null"); } } public static void notEmpty(final CharSequence s, final String name) { if (TextUtils.isEmpty(s)) { throw new IllegalStateException(name + " is empty"); } } public static void notBlank(final CharSequence s, final String name) { if (TextUtils.isBlank(s)) { throw new IllegalStateException(name + " is blank"); } } } httpcore5/src/main/java/org/apache/hc/core5/util/Timeout.java0100664 0000000 0000000 00000014610 14435411677 023063 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.text.ParseException; import java.time.Duration; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Represents a timeout value as a non-negative {@code long} time and {@link TimeUnit}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class Timeout extends TimeValue { /** * A zero milliseconds {@link Timeout}. */ public static final Timeout ZERO_MILLISECONDS = Timeout.of(0, TimeUnit.MILLISECONDS); /** * A one milliseconds {@link Timeout}. */ public static final Timeout ONE_MILLISECOND = Timeout.of(1, TimeUnit.MILLISECONDS); /** * A disabled timeout represented as 0 {@code MILLISECONDS}. */ public static final Timeout DISABLED = ZERO_MILLISECONDS; /** * Returns the given {@code timeout} if it is not {@code null}, if {@code null} then returns {@link #DISABLED}. * * @param timeout may be {@code null} * @return {@code timeValue} or {@link #DISABLED} */ public static Timeout defaultsToDisabled(final Timeout timeout) { return defaultsTo(timeout, DISABLED); } /** * Creates a Timeout from a Duration. * * @param duration the time duration. * @return a Timeout. * @since 5.2 */ public static Timeout of(final Duration duration) { final long seconds = duration.getSeconds(); final long nanoOfSecond = duration.getNano(); if (seconds == 0) { // no conversion return of(nanoOfSecond, TimeUnit.NANOSECONDS); } else if (nanoOfSecond == 0) { // no conversion return of(seconds, TimeUnit.SECONDS); } // conversion attempts try { return of(duration.toNanos(), TimeUnit.NANOSECONDS); } catch (final ArithmeticException e) { try { return of(duration.toMillis(), TimeUnit.MILLISECONDS); } catch (final ArithmeticException e1) { // backstop return of(seconds, TimeUnit.SECONDS); } } } /** * Creates a Timeout. * * @param duration the time duration. * @param timeUnit the time unit for the given duration. * @return a Timeout */ public static Timeout of(final long duration, final TimeUnit timeUnit) { return new Timeout(duration, timeUnit); } /** * Creates a Timeout. * * @param days the duration in days and the given {@code timeUnit}. * @return a Timeout */ public static Timeout ofDays(final long days) { return of(days, TimeUnit.DAYS); } /** * Creates a Timeout. * * @param hours the duration in hours and the given {@code timeUnit}. * @return a Timeout */ public static Timeout ofHours(final long hours) { return of(hours, TimeUnit.HOURS); } /** * Creates a Timeout. * * @param microseconds the duration in seconds and the given {@code timeUnit}. * @return a Timeout */ public static Timeout ofMicroseconds(final long microseconds) { return of(microseconds, TimeUnit.MICROSECONDS); } /** * Creates a Timeout. * * @param milliseconds the duration in milliseconds and the given {@code timeUnit}. * @return a Timeout */ public static Timeout ofMilliseconds(final long milliseconds) { return of(milliseconds, TimeUnit.MILLISECONDS); } /** * Creates a Timeout. * * @param minutes the duration in minutes and the given {@code timeUnit}. * @return a Timeout */ public static Timeout ofMinutes(final long minutes) { return of(minutes, TimeUnit.MINUTES); } /** * Creates a Timeout. * * @param nanoseconds the duration in seconds and the given {@code timeUnit}. * @return a Timeout */ public static Timeout ofNanoseconds(final long nanoseconds) { return of(nanoseconds, TimeUnit.NANOSECONDS); } /** * Creates a Timeout. * * @param seconds the duration in seconds and the given {@code timeUnit}. * @return a Timeout */ public static Timeout ofSeconds(final long seconds) { return of(seconds, TimeUnit.SECONDS); } /** * Parses a Timeout in the format {@code }, for example {@code "1,200 MILLISECONDS"} * * @param value the TimeValue to parse * @return a new TimeValue * @throws ParseException if the number cannot be parsed */ public static Timeout parse(final String value) throws ParseException { return TimeValue.parse(value).toTimeout(); } Timeout(final long duration, final TimeUnit timeUnit) { super(Args.notNegative(duration, "duration"), Args.notNull(timeUnit, "timeUnit")); } /** * Whether this timeout is disabled. * * @return Whether this timeout is disabled. */ public boolean isDisabled() { return getDuration() == 0; } /** * Whether this timeout is enabled. * * @return Whether this timeout is disabled. */ public boolean isEnabled() { return !isDisabled(); } } httpcore5/src/main/java/org/apache/hc/core5/util/TextUtils.java0100664 0000000 0000000 00000010316 14435411677 023401 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.util.Locale; /** * @since 4.3 */ public final class TextUtils { private TextUtils() { // Do not allow utility class to be instantiated. } /** * Returns true if the parameter is null or of zero length */ public static boolean isEmpty(final CharSequence s) { return length(s) == 0; } /** *

Checks if a CharSequence is empty (""), null or whitespace only.

* *

Whitespace is defined by {@link Character#isWhitespace(char)}.

* *
     * TextUtils.isBlank(null)      = true
     * TextUtils.isBlank("")        = true
     * TextUtils.isBlank(" ")       = true
     * TextUtils.isBlank("abg")     = false
     * TextUtils.isBlank("  abg  ") = false
     * 
* * @param s the CharSequence to check, may be null * @return {@code true} if the CharSequence is null, empty or whitespace only */ public static boolean isBlank(final CharSequence s) { final int strLen = length(s); if (strLen == 0) { return true; } for (int i = 0; i < strLen; i++) { if (!Character.isWhitespace(s.charAt(i))) { return false; } } return true; } /** * Gets a CharSequence length or {@code 0} if the CharSequence is * {@code null}. * * @param cs * a CharSequence or {@code null} * @return CharSequence length or {@code 0} if the CharSequence is * {@code null}. * @since 5.1 */ public static int length(final CharSequence cs) { return cs == null ? 0 : cs.length(); } /** * @since 4.4 */ public static boolean containsBlanks(final CharSequence s) { final int strLen = length(s); if (strLen == 0) { return false; } for (int i = 0; i < s.length(); i++) { if (Character.isWhitespace(s.charAt(i))) { return true; } } return false; } /** * Returns a hexadecimal string with lowercase letters, representing the * values of the {@code bytes}. * * @param bytes whose hex string should be created * @return hex string for the bytes * * @since 5.0 */ public static String toHexString(final byte[] bytes) { if (bytes == null) { return null; } final StringBuilder buffer = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { final int unsignedB = bytes[i] & 0xff; if (unsignedB < 16) { buffer.append('0'); } buffer.append(Integer.toHexString(unsignedB)); } return buffer.toString(); } /** * Returns lower case representation of the given string * using {@link Locale#ROOT}. * * @since 5.2 */ public static String toLowerCase(final String s) { if (s == null) { return null; } return s.toLowerCase(Locale.ROOT); } } httpcore5/src/main/java/org/apache/hc/core5/util/Tokenizer.java0100664 0000000 0000000 00000030742 14403631147 023402 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.util.BitSet; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Tokenizer that can be used as a foundation for more complex parsing routines. * Methods of this class are designed to produce near zero intermediate garbage * and make no intermediate copies of input data. *

* This class is immutable and thread safe. * * @since 5.1 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class Tokenizer { public static class Cursor { private final int lowerBound; private final int upperBound; private int pos; public Cursor(final int lowerBound, final int upperBound) { super(); Args.notNegative(lowerBound, "lowerBound"); Args.check(lowerBound <= upperBound, "lowerBound cannot be greater than upperBound"); this.lowerBound = lowerBound; this.upperBound = upperBound; this.pos = lowerBound; } public int getLowerBound() { return this.lowerBound; } public int getUpperBound() { return this.upperBound; } public int getPos() { return this.pos; } public void updatePos(final int pos) { Args.check(pos >= this.lowerBound, "pos: %s < lowerBound: %s", pos, this.lowerBound); Args.check(pos <= this.upperBound, "pos: %s > upperBound: %s", pos, this.upperBound); this.pos = pos; } public boolean atEnd() { return this.pos >= this.upperBound; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append('['); buffer.append(this.lowerBound); buffer.append('>'); buffer.append(this.pos); buffer.append('>'); buffer.append(this.upperBound); buffer.append(']'); return buffer.toString(); } } public static BitSet INIT_BITSET(final int ... b) { final BitSet bitset = new BitSet(); for (final int aB : b) { bitset.set(aB); } return bitset; } /** Double quote */ public static final char DQUOTE = '\"'; /** Backward slash / escape character */ public static final char ESCAPE = '\\'; public static final int CR = 13; // public static final int LF = 10; // public static final int SP = 32; // public static final int HT = 9; // public static boolean isWhitespace(final char ch) { return ch == SP || ch == HT || ch == CR || ch == LF; } public static final Tokenizer INSTANCE = new Tokenizer(); /** * Extracts from the sequence of chars a token terminated with any of the given delimiters * or a whitespace characters. * * @param buf buffer with the sequence of chars to be parsed * @param cursor defines the bounds and current position of the buffer * @param delimiters set of delimiting characters. Can be {@code null} if the token * is not delimited by any character. */ public String parseContent(final CharSequence buf, final Cursor cursor, final BitSet delimiters) { Args.notNull(buf, "Char sequence"); Args.notNull(cursor, "Parser cursor"); final StringBuilder dst = new StringBuilder(); copyContent(buf, cursor, delimiters, dst); return dst.toString(); } /** * Extracts from the sequence of chars a token terminated with any of the given delimiters * discarding semantically insignificant whitespace characters. * * @param buf buffer with the sequence of chars to be parsed * @param cursor defines the bounds and current position of the buffer * @param delimiters set of delimiting characters. Can be {@code null} if the token * is not delimited by any character. */ public String parseToken(final CharSequence buf, final Cursor cursor, final BitSet delimiters) { Args.notNull(buf, "Char sequence"); Args.notNull(cursor, "Parser cursor"); final StringBuilder dst = new StringBuilder(); boolean whitespace = false; while (!cursor.atEnd()) { final char current = buf.charAt(cursor.getPos()); if (delimiters != null && delimiters.get(current)) { break; } else if (isWhitespace(current)) { skipWhiteSpace(buf, cursor); whitespace = true; } else { if (whitespace && dst.length() > 0) { dst.append(' '); } copyContent(buf, cursor, delimiters, dst); whitespace = false; } } return dst.toString(); } /** * Extracts from the sequence of chars a value which can be enclosed in quote marks and * terminated with any of the given delimiters discarding semantically insignificant * whitespace characters. * * @param buf buffer with the sequence of chars to be parsed * @param cursor defines the bounds and current position of the buffer * @param delimiters set of delimiting characters. Can be {@code null} if the value * is not delimited by any character. */ public String parseValue(final CharSequence buf, final Cursor cursor, final BitSet delimiters) { Args.notNull(buf, "Char sequence"); Args.notNull(cursor, "Parser cursor"); final StringBuilder dst = new StringBuilder(); boolean whitespace = false; while (!cursor.atEnd()) { final char current = buf.charAt(cursor.getPos()); if (delimiters != null && delimiters.get(current)) { break; } else if (isWhitespace(current)) { skipWhiteSpace(buf, cursor); whitespace = true; } else if (current == DQUOTE) { if (whitespace && dst.length() > 0) { dst.append(' '); } copyQuotedContent(buf, cursor, dst); whitespace = false; } else { if (whitespace && dst.length() > 0) { dst.append(' '); } copyUnquotedContent(buf, cursor, delimiters, dst); whitespace = false; } } return dst.toString(); } /** * Skips semantically insignificant whitespace characters and moves the cursor to the closest * non-whitespace character. * * @param buf buffer with the sequence of chars to be parsed * @param cursor defines the bounds and current position of the buffer */ public void skipWhiteSpace(final CharSequence buf, final Cursor cursor) { Args.notNull(buf, "Char sequence"); Args.notNull(cursor, "Parser cursor"); int pos = cursor.getPos(); final int indexFrom = cursor.getPos(); final int indexTo = cursor.getUpperBound(); for (int i = indexFrom; i < indexTo; i++) { final char current = buf.charAt(i); if (!isWhitespace(current)) { break; } pos++; } cursor.updatePos(pos); } /** * Transfers content into the destination buffer until a whitespace character or any of * the given delimiters is encountered. * * @param buf buffer with the sequence of chars to be parsed * @param cursor defines the bounds and current position of the buffer * @param delimiters set of delimiting characters. Can be {@code null} if the value * is delimited by a whitespace only. * @param dst destination buffer */ public void copyContent(final CharSequence buf, final Cursor cursor, final BitSet delimiters, final StringBuilder dst) { Args.notNull(buf, "Char sequence"); Args.notNull(cursor, "Parser cursor"); Args.notNull(dst, "String builder"); int pos = cursor.getPos(); final int indexFrom = cursor.getPos(); final int indexTo = cursor.getUpperBound(); for (int i = indexFrom; i < indexTo; i++) { final char current = buf.charAt(i); if ((delimiters != null && delimiters.get(current)) || isWhitespace(current)) { break; } pos++; dst.append(current); } cursor.updatePos(pos); } /** * Transfers content into the destination buffer until a whitespace character, a quote, * or any of the given delimiters is encountered. * * @param buf buffer with the sequence of chars to be parsed * @param cursor defines the bounds and current position of the buffer * @param delimiters set of delimiting characters. Can be {@code null} if the value * is delimited by a whitespace or a quote only. * @param dst destination buffer */ public void copyUnquotedContent(final CharSequence buf, final Cursor cursor, final BitSet delimiters, final StringBuilder dst) { Args.notNull(buf, "Char sequence"); Args.notNull(cursor, "Parser cursor"); Args.notNull(dst, "String builder"); int pos = cursor.getPos(); final int indexFrom = cursor.getPos(); final int indexTo = cursor.getUpperBound(); for (int i = indexFrom; i < indexTo; i++) { final char current = buf.charAt(i); if ((delimiters != null && delimiters.get(current)) || isWhitespace(current) || current == DQUOTE) { break; } pos++; dst.append(current); } cursor.updatePos(pos); } /** * Transfers content enclosed with quote marks into the destination buffer. * * @param buf buffer with the sequence of chars to be parsed * @param cursor defines the bounds and current position of the buffer * @param dst destination buffer */ public void copyQuotedContent(final CharSequence buf, final Cursor cursor, final StringBuilder dst) { Args.notNull(buf, "Char sequence"); Args.notNull(cursor, "Parser cursor"); Args.notNull(dst, "String builder"); if (cursor.atEnd()) { return; } int pos = cursor.getPos(); int indexFrom = cursor.getPos(); final int indexTo = cursor.getUpperBound(); char current = buf.charAt(pos); if (current != DQUOTE) { return; } pos++; indexFrom++; boolean escaped = false; for (int i = indexFrom; i < indexTo; i++, pos++) { current = buf.charAt(i); if (escaped) { if (current != DQUOTE && current != ESCAPE) { dst.append(ESCAPE); } dst.append(current); escaped = false; } else { if (current == DQUOTE) { pos++; break; } if (current == ESCAPE) { escaped = true; } else if (current != CR && current != LF) { dst.append(current); } } } cursor.updatePos(pos); } } httpcore5/src/main/java/org/apache/hc/core5/pool/0040775 0000000 0000000 00000000000 14403631147 020556 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/pool/package-info.java0100664 0000000 0000000 00000002372 14245617503 023752 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Client side connection pool component APIs. */ package org.apache.hc.core5.pool; httpcore5/src/main/java/org/apache/hc/core5/pool/ManagedConnPool.java0100664 0000000 0000000 00000003163 14245617503 024431 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import org.apache.hc.core5.io.ModalCloseable; /** * {@link ConnPool} that also implements {@link ConnPoolControl} and {@link AutoCloseable}. * * @param the route type that represents the opposite endpoint of a pooled * connection. * @param the type of pooled connections. * @since 4.2 */ public interface ManagedConnPool extends ConnPool, ConnPoolControl, ModalCloseable { } httpcore5/src/main/java/org/apache/hc/core5/pool/ConnPoolStats.java0100664 0000000 0000000 00000002736 14245617503 024200 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; /** * Interface to obtain connection pool statistics. * * @param the route type that represents the opposite endpoint of a pooled * connection. * @since 4.2 */ public interface ConnPoolStats { PoolStats getTotalStats(); PoolStats getStats(final T route); } httpcore5/src/main/java/org/apache/hc/core5/pool/ConnPoolListener.java0100664 0000000 0000000 00000003100 14245617503 024651 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Connection pool event listener. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface ConnPoolListener { void onLease(T route, ConnPoolStats connPoolStats); void onRelease(T route, ConnPoolStats connPoolStats); } httpcore5/src/main/java/org/apache/hc/core5/pool/PoolStats.java0100664 0000000 0000000 00000007335 14245617503 023362 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import java.io.Serializable; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Pool statistics. *

* The total number of connections in the pool is equal to {@code available} plus {@code leased}. *

* * @since 4.2 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class PoolStats implements Serializable { private static final long serialVersionUID = -2807686144795228544L; private final int leased; private final int pending; private final int available; private final int max; public PoolStats(final int leased, final int pending, final int free, final int max) { super(); this.leased = leased; this.pending = pending; this.available = free; this.max = max; } /** * Gets the number of persistent connections tracked by the connection manager currently being used to execute * requests. *

* The total number of connections in the pool is equal to {@code available} plus {@code leased}. *

* * @return the number of persistent connections. */ public int getLeased() { return this.leased; } /** * Gets the number of connection requests being blocked awaiting a free connection. This can happen only if there * are more worker threads contending for fewer connections. * * @return the number of connection requests being blocked awaiting a free connection. */ public int getPending() { return this.pending; } /** * Gets the number idle persistent connections. *

* The total number of connections in the pool is equal to {@code available} plus {@code leased}. *

* * @return number idle persistent connections. */ public int getAvailable() { return this.available; } /** * Gets the maximum number of allowed persistent connections. * * @return the maximum number of allowed persistent connections. */ public int getMax() { return this.max; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("[leased: "); buffer.append(this.leased); buffer.append("; pending: "); buffer.append(this.pending); buffer.append("; available: "); buffer.append(this.available); buffer.append("; max: "); buffer.append(this.max); buffer.append("]"); return buffer.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/pool/ConnPool.java0100664 0000000 0000000 00000005667 14245617503 023167 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Timeout; /** * {@code ConnPool} represents a shared pool connections can be leased from * and released back to. * * @param the route type that represents the opposite endpoint of a pooled * connection. * @param the type of pooled connections. * @since 4.2 */ public interface ConnPool { /** * Attempts to lease a connection for the given route and with the given * state from the pool. *

* Please note the connection request can get automatically cancelled by the pool * in case of a request timeout. * * @param route route of the connection. * @param state arbitrary object that represents a particular state * (usually a security principal or a unique token identifying * the user whose credentials have been used while establishing the connection). * May be {@code null}. * @param requestTimeout request timeout. In case of a timeout the request * can get automatically cancelled by the pool. * @param callback operation completion callback. * * @return future for a leased pool entry. */ Future> lease(T route, Object state, Timeout requestTimeout, FutureCallback> callback); /** * Releases the pool entry back to the pool. * * @param entry pool entry leased from the pool * @param reusable flag indicating whether or not the released connection * is in a consistent state and is safe for further use. */ void release(PoolEntry entry, boolean reusable); } httpcore5/src/main/java/org/apache/hc/core5/pool/DisposalCallback.java0100664 0000000 0000000 00000003154 14245617503 024620 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; /** * Represents a customizable disposal strategy for {@link ModalCloseable} instances. * * @param process or connection type. * * @since 5.0 */ @Internal public interface DisposalCallback { void execute(final T closeable, CloseMode closeMode); } httpcore5/src/main/java/org/apache/hc/core5/pool/DefaultDisposalCallback.java0100664 0000000 0000000 00000004311 14245617503 026121 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.SocketModalCloseable; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Default implementation of {@link DisposalCallback}. * * @param socket based connection type. * * @since 5.0 */ @Internal public final class DefaultDisposalCallback implements DisposalCallback { private final static Timeout DEFAULT_CLOSE_TIMEOUT = Timeout.ofSeconds(1L); @Override public void execute(final SocketModalCloseable closeable, final CloseMode closeMode) { final Timeout socketTimeout = closeable.getSocketTimeout(); if (socketTimeout == null || socketTimeout.compareTo(TimeValue.ZERO_MILLISECONDS) <= 0 || socketTimeout.compareTo(DEFAULT_CLOSE_TIMEOUT) > 0) { closeable.setSocketTimeout(DEFAULT_CLOSE_TIMEOUT); } closeable.close(closeMode); } } httpcore5/src/main/java/org/apache/hc/core5/pool/LaxConnPool.java0100664 0000000 0000000 00000055602 14403631147 023622 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import java.util.Deque; import java.util.HashSet; import java.util.Iterator; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicMarkableReference; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.Deadline; import org.apache.hc.core5.util.DeadlineTimeoutException; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Connection pool with higher concurrency but with lax connection limit guarantees. * * @param route * @param connection object * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) @Experimental public class LaxConnPool implements ManagedConnPool { private final TimeValue timeToLive; private final PoolReusePolicy policy; private final DisposalCallback disposalCallback; private final ConnPoolListener connPoolListener; private final ConcurrentMap> routeToPool; private final AtomicBoolean isShutDown; private volatile int defaultMaxPerRoute; /** * @since 5.0 */ public LaxConnPool( final int defaultMaxPerRoute, final TimeValue timeToLive, final PoolReusePolicy policy, final DisposalCallback disposalCallback, final ConnPoolListener connPoolListener) { super(); Args.positive(defaultMaxPerRoute, "Max per route value"); this.timeToLive = TimeValue.defaultsToNegativeOneMillisecond(timeToLive); this.policy = policy != null ? policy : PoolReusePolicy.LIFO; this.disposalCallback = disposalCallback; this.connPoolListener = connPoolListener; this.routeToPool = new ConcurrentHashMap<>(); this.isShutDown = new AtomicBoolean(false); this.defaultMaxPerRoute = defaultMaxPerRoute; } /** * @since 5.0 */ public LaxConnPool( final int defaultMaxPerRoute, final TimeValue timeToLive, final PoolReusePolicy policy, final ConnPoolListener connPoolListener) { this(defaultMaxPerRoute, timeToLive, policy, null, connPoolListener); } public LaxConnPool(final int defaultMaxPerRoute) { this(defaultMaxPerRoute, TimeValue.NEG_ONE_MILLISECOND, PoolReusePolicy.LIFO, null, null); } public boolean isShutdown() { return isShutDown.get(); } @Override public void close(final CloseMode closeMode) { if (isShutDown.compareAndSet(false, true)) { for (final Iterator> it = routeToPool.values().iterator(); it.hasNext(); ) { final PerRoutePool routePool = it.next(); routePool.shutdown(closeMode); } routeToPool.clear(); } } @Override public void close() { close(CloseMode.GRACEFUL); } private PerRoutePool getPool(final T route) { PerRoutePool routePool = routeToPool.get(route); if (routePool == null) { final PerRoutePool newRoutePool = new PerRoutePool<>( route, defaultMaxPerRoute, timeToLive, policy, this, disposalCallback, connPoolListener); routePool = routeToPool.putIfAbsent(route, newRoutePool); if (routePool == null) { routePool = newRoutePool; } } return routePool; } @Override public Future> lease( final T route, final Object state, final Timeout requestTimeout, final FutureCallback> callback) { Args.notNull(route, "Route"); Asserts.check(!isShutDown.get(), "Connection pool shut down"); final PerRoutePool routePool = getPool(route); return routePool.lease(state, requestTimeout, callback); } public Future> lease(final T route, final Object state) { return lease(route, state, Timeout.DISABLED, null); } @Override public void release(final PoolEntry entry, final boolean reusable) { if (entry == null) { return; } if (isShutDown.get()) { return; } final PerRoutePool routePool = getPool(entry.getRoute()); routePool.release(entry, reusable); } public void validatePendingRequests() { for (final PerRoutePool routePool : routeToPool.values()) { routePool.validatePendingRequests(); } } @Override public void setMaxTotal(final int max) { } @Override public int getMaxTotal() { return 0; } @Override public void setDefaultMaxPerRoute(final int max) { Args.positive(max, "Max value"); defaultMaxPerRoute = max; } @Override public int getDefaultMaxPerRoute() { return defaultMaxPerRoute; } @Override public void setMaxPerRoute(final T route, final int max) { Args.notNull(route, "Route"); final PerRoutePool routePool = getPool(route); routePool.setMax(max > -1 ? max : defaultMaxPerRoute); } @Override public int getMaxPerRoute(final T route) { Args.notNull(route, "Route"); final PerRoutePool routePool = getPool(route); return routePool.getMax(); } @Override public PoolStats getTotalStats() { int leasedTotal = 0; int pendingTotal = 0; int availableTotal = 0; int maxTotal = 0; for (final PerRoutePool routePool : routeToPool.values()) { leasedTotal += routePool.getLeasedCount(); pendingTotal += routePool.getPendingCount(); availableTotal += routePool.getAvailableCount(); maxTotal += routePool.getMax(); } return new PoolStats(leasedTotal, pendingTotal, availableTotal, maxTotal); } @Override public PoolStats getStats(final T route) { Args.notNull(route, "Route"); final PerRoutePool routePool = getPool(route); return new PoolStats( routePool.getLeasedCount(), routePool.getPendingCount(), routePool.getAvailableCount(), routePool.getMax()); } @Override public Set getRoutes() { return new HashSet<>(routeToPool.keySet()); } public void enumAvailable(final Callback> callback) { for (final PerRoutePool routePool : routeToPool.values()) { routePool.enumAvailable(callback); } } public void enumLeased(final Callback> callback) { for (final PerRoutePool routePool : routeToPool.values()) { routePool.enumLeased(callback); } } @Override public void closeIdle(final TimeValue idleTime) { final long deadline = System.currentTimeMillis() - (TimeValue.isPositive(idleTime) ? idleTime.toMilliseconds() : 0); enumAvailable(entry -> { if (entry.getUpdated() <= deadline) { entry.discardConnection(CloseMode.GRACEFUL); } }); } @Override public void closeExpired() { final long now = System.currentTimeMillis(); enumAvailable(entry -> { if (entry.getExpiryDeadline().isBefore(now)) { entry.discardConnection(CloseMode.GRACEFUL); } }); } @Override public String toString() { final PoolStats totalStats = getTotalStats(); final StringBuilder buffer = new StringBuilder(); buffer.append("[leased: "); buffer.append(totalStats.getLeased()); buffer.append("][available: "); buffer.append(totalStats.getAvailable()); buffer.append("][pending: "); buffer.append(totalStats.getPending()); buffer.append("]"); return buffer.toString(); } static class LeaseRequest implements Cancellable { private final Object state; private final Deadline deadline; private final BasicFuture> future; LeaseRequest( final Object state, final Timeout requestTimeout, final BasicFuture> future) { super(); this.state = state; this.deadline = Deadline.calculate(requestTimeout); this.future = future; } BasicFuture> getFuture() { return this.future; } public Object getState() { return this.state; } public Deadline getDeadline() { return this.deadline; } public boolean isDone() { return this.future.isDone(); } public boolean completed(final PoolEntry result) { return future.completed(result); } public boolean failed(final Exception ex) { return future.failed(ex); } @Override public boolean cancel() { return future.cancel(); } } static class PerRoutePool { private enum RequestServiceStrategy { FIRST_SUCCESSFUL, ALL } private final T route; private final TimeValue timeToLive; private final PoolReusePolicy policy; private final DisposalCallback disposalCallback; private final ConnPoolListener connPoolListener; private final ConnPoolStats connPoolStats; private final ConcurrentMap, Boolean> leased; private final Deque>> available; private final Deque> pending; private final AtomicBoolean terminated; private final AtomicInteger allocated; private final AtomicLong releaseSeqNum; private volatile int max; PerRoutePool( final T route, final int max, final TimeValue timeToLive, final PoolReusePolicy policy, final ConnPoolStats connPoolStats, final DisposalCallback disposalCallback, final ConnPoolListener connPoolListener) { super(); this.route = route; this.timeToLive = timeToLive; this.policy = policy; this.connPoolStats = connPoolStats; this.disposalCallback = disposalCallback; this.connPoolListener = connPoolListener; this.leased = new ConcurrentHashMap<>(); this.available = new ConcurrentLinkedDeque<>(); this.pending = new ConcurrentLinkedDeque<>(); this.terminated = new AtomicBoolean(false); this.allocated = new AtomicInteger(0); this.releaseSeqNum = new AtomicLong(0); this.max = max; } public void shutdown(final CloseMode closeMode) { if (terminated.compareAndSet(false, true)) { AtomicMarkableReference> entryRef; while ((entryRef = available.poll()) != null) { entryRef.getReference().discardConnection(closeMode); } for (final PoolEntry entry : leased.keySet()) { entry.discardConnection(closeMode); } leased.clear(); LeaseRequest leaseRequest; while ((leaseRequest = pending.poll()) != null) { leaseRequest.cancel(); } } } private PoolEntry createPoolEntry() { final int poolMax = max; int prev, next; do { prev = allocated.get(); next = (prev(route, timeToLive, disposalCallback) : null; } private void deallocatePoolEntry() { allocated.decrementAndGet(); } private void addLeased(final PoolEntry entry) { if (leased.putIfAbsent(entry, Boolean.TRUE) != null) { throw new IllegalStateException("Pool entry already present in the set of leased entries"); } else if (connPoolListener != null) { connPoolListener.onLease(route, connPoolStats); } } private void removeLeased(final PoolEntry entry) { if (connPoolListener != null) { connPoolListener.onRelease(route, connPoolStats); } if (!leased.remove(entry, Boolean.TRUE)) { throw new IllegalStateException("Pool entry is not present in the set of leased entries"); } } private PoolEntry getAvailableEntry(final Object state) { for (final Iterator>> it = available.iterator(); it.hasNext(); ) { final AtomicMarkableReference> ref = it.next(); final PoolEntry entry = ref.getReference(); if (ref.compareAndSet(entry, entry, false, true)) { it.remove(); if (entry.getExpiryDeadline().isExpired()) { entry.discardConnection(CloseMode.GRACEFUL); } if (!Objects.equals(entry.getState(), state)) { entry.discardConnection(CloseMode.GRACEFUL); } return entry; } } return null; } public Future> lease( final Object state, final Timeout requestTimeout, final FutureCallback> callback) { Asserts.check(!terminated.get(), "Connection pool shut down"); final BasicFuture> future = new BasicFuture>(callback) { @Override public synchronized PoolEntry get( final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { try { return super.get(timeout, unit); } catch (final TimeoutException ex) { cancel(); throw ex; } } }; final long releaseState = releaseSeqNum.get(); PoolEntry entry = null; if (pending.isEmpty()) { entry = getAvailableEntry(state); if (entry == null) { entry = createPoolEntry(); } } if (entry != null) { addLeased(entry); future.completed(entry); } else { pending.add(new LeaseRequest<>(state, requestTimeout, future)); if (releaseState != releaseSeqNum.get()) { servicePendingRequest(); } } return future; } public void release(final PoolEntry releasedEntry, final boolean reusable) { removeLeased(releasedEntry); if (!reusable || releasedEntry.getExpiryDeadline().isExpired()) { releasedEntry.discardConnection(CloseMode.GRACEFUL); } if (releasedEntry.hasConnection()) { switch (policy) { case LIFO: available.addFirst(new AtomicMarkableReference<>(releasedEntry, false)); break; case FIFO: available.addLast(new AtomicMarkableReference<>(releasedEntry, false)); break; default: throw new IllegalStateException("Unexpected ConnPoolPolicy value: " + policy); } } else { deallocatePoolEntry(); } releaseSeqNum.incrementAndGet(); servicePendingRequest(); } private void servicePendingRequest() { servicePendingRequests(RequestServiceStrategy.FIRST_SUCCESSFUL); } private void servicePendingRequests(final RequestServiceStrategy serviceStrategy) { LeaseRequest leaseRequest; while ((leaseRequest = pending.poll()) != null) { if (leaseRequest.isDone()) { continue; } final Object state = leaseRequest.getState(); final Deadline deadline = leaseRequest.getDeadline(); if (deadline.isExpired()) { leaseRequest.failed(DeadlineTimeoutException.from(deadline)); } else { final long releaseState = releaseSeqNum.get(); PoolEntry entry = getAvailableEntry(state); if (entry == null) { entry = createPoolEntry(); } if (entry != null) { addLeased(entry); if (!leaseRequest.completed(entry)) { release(entry, true); } if (serviceStrategy == RequestServiceStrategy.FIRST_SUCCESSFUL) { break; } } else { pending.addFirst(leaseRequest); if (releaseState == releaseSeqNum.get()) { break; } } } } } public void validatePendingRequests() { final Iterator> it = pending.iterator(); while (it.hasNext()) { final LeaseRequest request = it.next(); final BasicFuture> future = request.getFuture(); if (future.isCancelled() && !request.isDone()) { it.remove(); } else { final Deadline deadline = request.getDeadline(); if (deadline.isExpired()) { request.failed(DeadlineTimeoutException.from(deadline)); } if (request.isDone()) { it.remove(); } } } } public final T getRoute() { return route; } public int getMax() { return max; } public void setMax(final int max) { this.max = max; } public int getPendingCount() { return pending.size(); } public int getLeasedCount() { return leased.size(); } public int getAvailableCount() { return available.size(); } public void enumAvailable(final Callback> callback) { for (final Iterator>> it = available.iterator(); it.hasNext(); ) { final AtomicMarkableReference> ref = it.next(); final PoolEntry entry = ref.getReference(); if (ref.compareAndSet(entry, entry, false, true)) { callback.execute(entry); if (!entry.hasConnection()) { deallocatePoolEntry(); it.remove(); } else { ref.set(entry, false); } } } releaseSeqNum.incrementAndGet(); servicePendingRequests(RequestServiceStrategy.ALL); } public void enumLeased(final Callback> callback) { for (final Iterator> it = leased.keySet().iterator(); it.hasNext(); ) { final PoolEntry entry = it.next(); callback.execute(entry); if (!entry.hasConnection()) { deallocatePoolEntry(); it.remove(); } } } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("[route: "); buffer.append(route); buffer.append("][leased: "); buffer.append(leased.size()); buffer.append("][available: "); buffer.append(available.size()); buffer.append("][pending: "); buffer.append(pending.size()); buffer.append("]"); return buffer.toString(); } } } httpcore5/src/main/java/org/apache/hc/core5/pool/PoolReusePolicy.java0100664 0000000 0000000 00000003050 14245617503 024515 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; /** * Enumeration of pooled connection re-use policies * * @since 5.0 */ public enum PoolReusePolicy { /** * Re-use as few connections as possible making it possible for connections to become idle and expire. */ LIFO, /** * Re-use all connections equally preventing them from becoming idle and expiring. */ FIFO } httpcore5/src/main/java/org/apache/hc/core5/pool/PoolEntry.java0100664 0000000 0000000 00000015467 14403631147 023366 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Deadline; import org.apache.hc.core5.util.TimeValue; /** * Pool entry containing a pool connection object along with its route. *

* The connection assigned to this pool entry may have an expiration time and also have an object * representing a connection state (usually a security principal or a unique token identifying * the user whose credentials have been used while establishing the connection). * * @param the route type that represents the opposite endpoint of a pooled * connection. * @param the connection type. * @since 4.2 */ public final class PoolEntry { private final T route; private final TimeValue timeToLive; private final AtomicReference connRef; private final DisposalCallback disposalCallback; private final Supplier currentTimeSupplier; private volatile Object state; private volatile long created; private volatile long updated; private volatile Deadline expiryDeadline = Deadline.MIN_VALUE; private volatile Deadline validityDeadline = Deadline.MIN_VALUE; PoolEntry(final T route, final TimeValue timeToLive, final DisposalCallback disposalCallback, final Supplier currentTimeSupplier) { super(); this.route = Args.notNull(route, "Route"); this.timeToLive = TimeValue.defaultsToNegativeOneMillisecond(timeToLive); this.connRef = new AtomicReference<>(); this.disposalCallback = disposalCallback; this.currentTimeSupplier = currentTimeSupplier; } PoolEntry(final T route, final TimeValue timeToLive, final Supplier currentTimeSupplier) { this(route, timeToLive, null, currentTimeSupplier); } /** * Creates new {@code PoolEntry} instance. * * @param route route to the opposite endpoint. * @param timeToLive maximum time to live. May be zero if the connection * does not have an expiry deadline. * @param disposalCallback callback invoked before connection disposal. */ public PoolEntry(final T route, final TimeValue timeToLive, final DisposalCallback disposalCallback) { this(route, timeToLive, disposalCallback, null); } /** * Creates new {@code PoolEntry} instance. * * @param route route to the opposite endpoint. * @param timeToLive maximum time to live. May be zero if the connection * does not have an expiry deadline. */ public PoolEntry(final T route, final TimeValue timeToLive) { this(route, timeToLive, null, null); } public PoolEntry(final T route) { this(route, null); } long getCurrentTime() { return currentTimeSupplier != null ? currentTimeSupplier.get() : System.currentTimeMillis(); } public T getRoute() { return this.route; } public C getConnection() { return this.connRef.get(); } /** * @since 5.0 */ public Deadline getValidityDeadline() { return this.validityDeadline; } public Object getState() { return this.state; } /** * @since 5.2 */ public long getCreated() { return created; } public long getUpdated() { return this.updated; } public Deadline getExpiryDeadline() { return this.expiryDeadline; } /** * @since 5.0 */ public boolean hasConnection() { return this.connRef.get() != null; } /** * @since 5.0 */ public void assignConnection(final C conn) { Args.notNull(conn, "connection"); if (this.connRef.compareAndSet(null, conn)) { this.created = getCurrentTime(); this.updated = this.created; this.validityDeadline = Deadline.calculate(this.created, this.timeToLive); this.expiryDeadline = this.validityDeadline; this.state = null; } else { throw new IllegalStateException("Connection already assigned"); } } /** * @since 5.0 */ public void discardConnection(final CloseMode closeMode) { final C connection = this.connRef.getAndSet(null); if (connection != null) { this.state = null; this.created = 0; this.updated = 0; this.expiryDeadline = Deadline.MIN_VALUE; this.validityDeadline = Deadline.MIN_VALUE; if (this.disposalCallback != null) { this.disposalCallback.execute(connection, closeMode); } else { connection.close(closeMode); } } } /** * @since 5.0 */ public void updateExpiry(final TimeValue expiryTime) { Args.notNull(expiryTime, "Expiry time"); final long currentTime = getCurrentTime(); final Deadline newExpiry = Deadline.calculate(currentTime, expiryTime); this.expiryDeadline = newExpiry.min(this.validityDeadline); this.updated = currentTime; } /** * @since 5.0 */ public void updateState(final Object state) { this.state = state; this.updated = getCurrentTime(); } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("[route:"); buffer.append(this.route); buffer.append("][state:"); buffer.append(this.state); buffer.append("]"); return buffer.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java0100664 0000000 0000000 00000067147 14403631147 024355 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.ListIterator; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.Deadline; import org.apache.hc.core5.util.DeadlineTimeoutException; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Connection pool with strict connection limit guarantees. * * @param route * @param connection object * * @since 4.2 */ @Contract(threading = ThreadingBehavior.SAFE) public class StrictConnPool implements ManagedConnPool { private final TimeValue timeToLive; private final PoolReusePolicy policy; private final DisposalCallback disposalCallback; private final ConnPoolListener connPoolListener; private final Map> routeToPool; private final LinkedList> pendingRequests; private final Set> leased; private final LinkedList> available; private final ConcurrentLinkedQueue> completedRequests; private final Map maxPerRoute; private final Lock lock; private final AtomicBoolean isShutDown; private volatile int defaultMaxPerRoute; private volatile int maxTotal; /** * @since 5.0 */ public StrictConnPool( final int defaultMaxPerRoute, final int maxTotal, final TimeValue timeToLive, final PoolReusePolicy policy, final DisposalCallback disposalCallback, final ConnPoolListener connPoolListener) { super(); Args.positive(defaultMaxPerRoute, "Max per route value"); Args.positive(maxTotal, "Max total value"); this.timeToLive = TimeValue.defaultsToNegativeOneMillisecond(timeToLive); this.policy = policy != null ? policy : PoolReusePolicy.LIFO; this.disposalCallback = disposalCallback; this.connPoolListener = connPoolListener; this.routeToPool = new HashMap<>(); this.pendingRequests = new LinkedList<>(); this.leased = new HashSet<>(); this.available = new LinkedList<>(); this.completedRequests = new ConcurrentLinkedQueue<>(); this.maxPerRoute = new HashMap<>(); this.lock = new ReentrantLock(); this.isShutDown = new AtomicBoolean(false); this.defaultMaxPerRoute = defaultMaxPerRoute; this.maxTotal = maxTotal; } /** * @since 5.0 */ public StrictConnPool( final int defaultMaxPerRoute, final int maxTotal, final TimeValue timeToLive, final PoolReusePolicy policy, final ConnPoolListener connPoolListener) { this(defaultMaxPerRoute, maxTotal, timeToLive, policy, null, connPoolListener); } public StrictConnPool(final int defaultMaxPerRoute, final int maxTotal) { this(defaultMaxPerRoute, maxTotal, TimeValue.NEG_ONE_MILLISECOND, PoolReusePolicy.LIFO, null); } public boolean isShutdown() { return this.isShutDown.get(); } @Override public void close(final CloseMode closeMode) { if (this.isShutDown.compareAndSet(false, true)) { fireCallbacks(); this.lock.lock(); try { for (final PerRoutePool pool: this.routeToPool.values()) { pool.shutdown(closeMode); } this.routeToPool.clear(); this.leased.clear(); this.available.clear(); this.pendingRequests.clear(); } finally { this.lock.unlock(); } } } @Override public void close() { close(CloseMode.GRACEFUL); } private PerRoutePool getPool(final T route) { PerRoutePool pool = this.routeToPool.get(route); if (pool == null) { pool = new PerRoutePool<>(route, this.disposalCallback); this.routeToPool.put(route, pool); } return pool; } @Override public Future> lease( final T route, final Object state, final Timeout requestTimeout, final FutureCallback> callback) { Args.notNull(route, "Route"); Args.notNull(requestTimeout, "Request timeout"); Asserts.check(!this.isShutDown.get(), "Connection pool shut down"); final Deadline deadline = Deadline.calculate(requestTimeout); final BasicFuture> future = new BasicFuture>(callback) { @Override public synchronized PoolEntry get( final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { try { return super.get(timeout, unit); } catch (final TimeoutException ex) { cancel(); throw ex; } } }; final boolean acquiredLock; try { acquiredLock = this.lock.tryLock(requestTimeout.getDuration(), requestTimeout.getTimeUnit()); } catch (final InterruptedException interruptedException) { Thread.currentThread().interrupt(); future.cancel(); return future; } if (acquiredLock) { try { final LeaseRequest request = new LeaseRequest<>(route, state, requestTimeout, future); final boolean completed = processPendingRequest(request); if (!request.isDone() && !completed) { this.pendingRequests.add(request); } if (request.isDone()) { this.completedRequests.add(request); } } finally { this.lock.unlock(); } fireCallbacks(); } else { future.failed(DeadlineTimeoutException.from(deadline)); } return future; } public Future> lease(final T route, final Object state) { return lease(route, state, Timeout.DISABLED, null); } @Override public void release(final PoolEntry entry, final boolean reusable) { if (entry == null) { return; } if (this.isShutDown.get()) { return; } if (!reusable) { entry.discardConnection(CloseMode.GRACEFUL); } this.lock.lock(); try { if (this.leased.remove(entry)) { if (this.connPoolListener != null) { this.connPoolListener.onRelease(entry.getRoute(), this); } final PerRoutePool pool = getPool(entry.getRoute()); final boolean keepAlive = entry.hasConnection() && reusable; pool.free(entry, keepAlive); if (keepAlive) { switch (policy) { case LIFO: this.available.addFirst(entry); break; case FIFO: this.available.addLast(entry); break; default: throw new IllegalStateException("Unexpected ConnPoolPolicy value: " + policy); } } else { entry.discardConnection(CloseMode.GRACEFUL); } processNextPendingRequest(); } else { throw new IllegalStateException("Pool entry is not present in the set of leased entries"); } } finally { this.lock.unlock(); } fireCallbacks(); } private void processPendingRequests() { final ListIterator> it = this.pendingRequests.listIterator(); while (it.hasNext()) { final LeaseRequest request = it.next(); final BasicFuture> future = request.getFuture(); if (future.isCancelled()) { it.remove(); continue; } final boolean completed = processPendingRequest(request); if (request.isDone() || completed) { it.remove(); } if (request.isDone()) { this.completedRequests.add(request); } } } private void processNextPendingRequest() { final ListIterator> it = this.pendingRequests.listIterator(); while (it.hasNext()) { final LeaseRequest request = it.next(); final BasicFuture> future = request.getFuture(); if (future.isCancelled()) { it.remove(); continue; } final boolean completed = processPendingRequest(request); if (request.isDone() || completed) { it.remove(); } if (request.isDone()) { this.completedRequests.add(request); } if (completed) { return; } } } private boolean processPendingRequest(final LeaseRequest request) { final T route = request.getRoute(); final Object state = request.getState(); final Deadline deadline = request.getDeadline(); if (deadline.isExpired()) { request.failed(DeadlineTimeoutException.from(deadline)); return false; } final PerRoutePool pool = getPool(route); PoolEntry entry; for (;;) { entry = pool.getFree(state); if (entry == null) { break; } if (entry.getExpiryDeadline().isExpired()) { entry.discardConnection(CloseMode.GRACEFUL); this.available.remove(entry); pool.free(entry, false); } else { break; } } if (entry != null) { this.available.remove(entry); this.leased.add(entry); request.completed(entry); if (this.connPoolListener != null) { this.connPoolListener.onLease(entry.getRoute(), this); } return true; } // New connection is needed final int maxPerRoute = getMax(route); // Shrink the pool prior to allocating a new connection final int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute); if (excess > 0) { for (int i = 0; i < excess; i++) { final PoolEntry lastUsed = pool.getLastUsed(); if (lastUsed == null) { break; } lastUsed.discardConnection(CloseMode.GRACEFUL); this.available.remove(lastUsed); pool.remove(lastUsed); } } if (pool.getAllocatedCount() < maxPerRoute) { final int freeCapacity = Math.max(this.maxTotal - this.leased.size(), 0); if (freeCapacity == 0) { return false; } final int totalAvailable = this.available.size(); if (totalAvailable > freeCapacity - 1) { final PoolEntry lastUsed = this.available.removeLast(); lastUsed.discardConnection(CloseMode.GRACEFUL); final PerRoutePool otherpool = getPool(lastUsed.getRoute()); otherpool.remove(lastUsed); } entry = pool.createEntry(this.timeToLive); this.leased.add(entry); request.completed(entry); if (this.connPoolListener != null) { this.connPoolListener.onLease(entry.getRoute(), this); } return true; } return false; } private void fireCallbacks() { LeaseRequest request; while ((request = this.completedRequests.poll()) != null) { final BasicFuture> future = request.getFuture(); final Exception ex = request.getException(); final PoolEntry result = request.getResult(); boolean successfullyCompleted = false; if (ex != null) { future.failed(ex); } else if (result != null) { if (future.completed(result)) { successfullyCompleted = true; } } else { future.cancel(); } if (!successfullyCompleted) { release(result, true); } } } public void validatePendingRequests() { this.lock.lock(); try { final long now = System.currentTimeMillis(); final ListIterator> it = this.pendingRequests.listIterator(); while (it.hasNext()) { final LeaseRequest request = it.next(); final BasicFuture> future = request.getFuture(); if (future.isCancelled() && !request.isDone()) { it.remove(); } else { final Deadline deadline = request.getDeadline(); if (deadline.isBefore(now)) { request.failed(DeadlineTimeoutException.from(deadline)); } if (request.isDone()) { it.remove(); this.completedRequests.add(request); } } } } finally { this.lock.unlock(); } fireCallbacks(); } private int getMax(final T route) { final Integer v = this.maxPerRoute.get(route); if (v != null) { return v; } return this.defaultMaxPerRoute; } @Override public void setMaxTotal(final int max) { Args.positive(max, "Max value"); this.lock.lock(); try { this.maxTotal = max; } finally { this.lock.unlock(); } } @Override public int getMaxTotal() { this.lock.lock(); try { return this.maxTotal; } finally { this.lock.unlock(); } } @Override public void setDefaultMaxPerRoute(final int max) { Args.positive(max, "Max value"); this.lock.lock(); try { this.defaultMaxPerRoute = max; } finally { this.lock.unlock(); } } @Override public int getDefaultMaxPerRoute() { this.lock.lock(); try { return this.defaultMaxPerRoute; } finally { this.lock.unlock(); } } @Override public void setMaxPerRoute(final T route, final int max) { Args.notNull(route, "Route"); this.lock.lock(); try { if (max > -1) { this.maxPerRoute.put(route, max); } else { this.maxPerRoute.remove(route); } } finally { this.lock.unlock(); } } @Override public int getMaxPerRoute(final T route) { Args.notNull(route, "Route"); this.lock.lock(); try { return getMax(route); } finally { this.lock.unlock(); } } @Override public PoolStats getTotalStats() { this.lock.lock(); try { return new PoolStats( this.leased.size(), this.pendingRequests.size(), this.available.size(), this.maxTotal); } finally { this.lock.unlock(); } } @Override public PoolStats getStats(final T route) { Args.notNull(route, "Route"); this.lock.lock(); try { final PerRoutePool pool = getPool(route); int pendingCount = 0; for (final LeaseRequest request: pendingRequests) { if (Objects.equals(route, request.getRoute())) { pendingCount++; } } return new PoolStats( pool.getLeasedCount(), pendingCount, pool.getAvailableCount(), getMax(route)); } finally { this.lock.unlock(); } } /** * Returns snapshot of all knows routes * * @since 4.4 */ @Override public Set getRoutes() { this.lock.lock(); try { return new HashSet<>(routeToPool.keySet()); } finally { this.lock.unlock(); } } /** * Enumerates all available connections. * * @since 4.3 */ public void enumAvailable(final Callback> callback) { this.lock.lock(); try { final Iterator> it = this.available.iterator(); while (it.hasNext()) { final PoolEntry entry = it.next(); callback.execute(entry); if (!entry.hasConnection()) { final PerRoutePool pool = getPool(entry.getRoute()); pool.remove(entry); it.remove(); } } processPendingRequests(); purgePoolMap(); } finally { this.lock.unlock(); } } /** * Enumerates all leased connections. * * @since 4.3 */ public void enumLeased(final Callback> callback) { this.lock.lock(); try { final Iterator> it = this.leased.iterator(); while (it.hasNext()) { final PoolEntry entry = it.next(); callback.execute(entry); } processPendingRequests(); } finally { this.lock.unlock(); } } private void purgePoolMap() { final Iterator>> it = this.routeToPool.entrySet().iterator(); while (it.hasNext()) { final Map.Entry> entry = it.next(); final PerRoutePool pool = entry.getValue(); if (pool.getAllocatedCount() == 0) { it.remove(); } } } @Override public void closeIdle(final TimeValue idleTime) { final long deadline = System.currentTimeMillis() - (TimeValue.isPositive(idleTime) ? idleTime.toMilliseconds() : 0); enumAvailable(entry -> { if (entry.getUpdated() <= deadline) { entry.discardConnection(CloseMode.GRACEFUL); } }); } @Override public void closeExpired() { final long now = System.currentTimeMillis(); enumAvailable(entry -> { if (entry.getExpiryDeadline().isBefore(now)) { entry.discardConnection(CloseMode.GRACEFUL); } }); } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("[leased: "); buffer.append(this.leased.size()); buffer.append("][available: "); buffer.append(this.available.size()); buffer.append("][pending: "); buffer.append(this.pendingRequests.size()); buffer.append("]"); return buffer.toString(); } static class LeaseRequest { private final T route; private final Object state; private final Deadline deadline; private final BasicFuture> future; // 'completed' is used internally to guard setting // 'result' and 'ex', but mustn't be used by 'isDone()'. private final AtomicBoolean completed; private volatile PoolEntry result; private volatile Exception ex; /** * Constructor * * @param route route * @param state state * @param requestTimeout timeout to wait in a request queue until kicked off * @param future future callback */ public LeaseRequest( final T route, final Object state, final Timeout requestTimeout, final BasicFuture> future) { super(); this.route = route; this.state = state; this.deadline = Deadline.calculate(requestTimeout); this.future = future; this.completed = new AtomicBoolean(false); } public T getRoute() { return this.route; } public Object getState() { return this.state; } public Deadline getDeadline() { return this.deadline; } public boolean isDone() { // This method must not use 'completed.get()' which would result in a race // where a caller may observe completed=true while neither result nor ex // have been set yet. return ex != null || result != null; } public void failed(final Exception ex) { if (this.completed.compareAndSet(false, true)) { this.ex = ex; } } public void completed(final PoolEntry result) { if (this.completed.compareAndSet(false, true)) { this.result = result; } } public BasicFuture> getFuture() { return this.future; } public PoolEntry getResult() { return this.result; } public Exception getException() { return this.ex; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("["); buffer.append(this.route); buffer.append("]["); buffer.append(this.state); buffer.append("]"); return buffer.toString(); } } static class PerRoutePool { private final T route; private final Set> leased; private final LinkedList> available; private final DisposalCallback disposalCallback; PerRoutePool(final T route, final DisposalCallback disposalCallback) { super(); this.route = route; this.disposalCallback = disposalCallback; this.leased = new HashSet<>(); this.available = new LinkedList<>(); } public final T getRoute() { return route; } public int getLeasedCount() { return this.leased.size(); } public int getAvailableCount() { return this.available.size(); } public int getAllocatedCount() { return this.available.size() + this.leased.size(); } public PoolEntry getFree(final Object state) { if (!this.available.isEmpty()) { if (state != null) { final Iterator> it = this.available.iterator(); while (it.hasNext()) { final PoolEntry entry = it.next(); if (state.equals(entry.getState())) { it.remove(); this.leased.add(entry); return entry; } } } final Iterator> it = this.available.iterator(); while (it.hasNext()) { final PoolEntry entry = it.next(); if (entry.getState() == null) { it.remove(); this.leased.add(entry); return entry; } } } return null; } public PoolEntry getLastUsed() { return this.available.peekLast(); } public boolean remove(final PoolEntry entry) { return this.available.remove(entry) || this.leased.remove(entry); } public void free(final PoolEntry entry, final boolean reusable) { final boolean found = this.leased.remove(entry); Asserts.check(found, "Entry %s has not been leased from this pool", entry); if (reusable) { this.available.addFirst(entry); } } public PoolEntry createEntry(final TimeValue timeToLive) { final PoolEntry entry = new PoolEntry<>(this.route, timeToLive, disposalCallback); this.leased.add(entry); return entry; } public void shutdown(final CloseMode closeMode) { PoolEntry availableEntry; while ((availableEntry = available.poll()) != null) { availableEntry.discardConnection(closeMode); } for (final PoolEntry entry: this.leased) { entry.discardConnection(closeMode); } this.leased.clear(); } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("[route: "); buffer.append(this.route); buffer.append("][leased: "); buffer.append(this.leased.size()); buffer.append("][available: "); buffer.append(this.available.size()); buffer.append("]"); return buffer.toString(); } } } httpcore5/src/main/java/org/apache/hc/core5/pool/PoolConcurrencyPolicy.java0100664 0000000 0000000 00000002734 14245617503 025734 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; /** * Enumeration of pool concurrency policies * * @since 5.0 */ public enum PoolConcurrencyPolicy { /** * Higher concurrency but with lax connection max limit guarantees. */ LAX, /** * Strict connection max limit guarantees. */ STRICT } httpcore5/src/main/java/org/apache/hc/core5/pool/ConnPoolControl.java0100664 0000000 0000000 00000003630 14245617503 024514 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import java.util.Set; import org.apache.hc.core5.util.TimeValue; /** * Interface to control runtime properties of a {@link ConnPool} such as * maximum total number of connections or maximum connections per route * allowed. * * @param the route type that represents the opposite endpoint of a pooled * connection. * @since 4.2 */ public interface ConnPoolControl extends ConnPoolStats { void setMaxTotal(int max); int getMaxTotal(); void setDefaultMaxPerRoute(int max); int getDefaultMaxPerRoute(); void setMaxPerRoute(final T route, int max); int getMaxPerRoute(final T route); void closeIdle(TimeValue idleTime); void closeExpired(); Set getRoutes(); } httpcore5/src/main/java/org/apache/hc/core5/annotation/0040775 0000000 0000000 00000000000 14245617503 021763 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/annotation/package-info.java0100664 0000000 0000000 00000002412 14245617503 025146 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Provides annotations for public interface definitions */ package org.apache.hc.core5.annotation; httpcore5/src/main/java/org/apache/hc/core5/annotation/Obsolete.java0100664 0000000 0000000 00000003322 14245617503 024377 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * The field or method to which this annotation is applied is marked as implementing * requirements of the HTTP protocol or a related protocol that are now obsolete. */ @Documented @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.CLASS) public @interface Obsolete { } httpcore5/src/main/java/org/apache/hc/core5/annotation/Contract.java0100664 0000000 0000000 00000003256 14245617503 024406 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation defines behavioral contract enforced at runtime by instances of annotated classes. */ @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface Contract { ThreadingBehavior threading() default ThreadingBehavior.UNSAFE; } httpcore5/src/main/java/org/apache/hc/core5/annotation/ThreadingBehavior.java0100664 0000000 0000000 00000004427 14245617503 026217 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.annotation; /** Defines types of threading behavior enforced at runtime. */ public enum ThreadingBehavior { /** * Instances of classes with the given contract are expected to be fully immutable * and thread-safe. */ IMMUTABLE, /** * Instances of classes with the given contract are expected to be immutable if their * dependencies injected at construction time are immutable and are expected to be thread-safe * if their dependencies are thread-safe. */ IMMUTABLE_CONDITIONAL, /** * Instances of classes with the given contract are expected to maintain no state * and to be thread-safe. */ STATELESS, /** * Instances of classes with the given contract are expected to be fully thread-safe. */ SAFE, /** * Instances of classes with the given contract are expected to be thread-safe if their * dependencies injected at construction time are thread-safe. */ SAFE_CONDITIONAL, /** * Instances of classes with the given contract are expected to be non thread-safe. */ UNSAFE } httpcore5/src/main/java/org/apache/hc/core5/annotation/Experimental.java0100664 0000000 0000000 00000003162 14245617503 025262 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * The field or method to which this annotation is applied is marked as experimental. */ @Documented @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.CLASS) public @interface Experimental { } httpcore5/src/main/java/org/apache/hc/core5/annotation/Internal.java0100664 0000000 0000000 00000003304 14245617503 024377 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * The package, class or method to which this annotation is applied is marked as internal * and its use ought to be avoided. */ @Documented @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PACKAGE}) @Retention(RetentionPolicy.CLASS) public @interface Internal { } httpcore5/src/main/java/org/apache/hc/core5/ssl/0040775 0000000 0000000 00000000000 14403631147 020406 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/ssl/package-info.java0100664 0000000 0000000 00000002452 14245617503 023601 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Utility classes for trust and key material management * and TLS/SSL context initialization. */ package org.apache.hc.core5.ssl; httpcore5/src/main/java/org/apache/hc/core5/ssl/SSLContexts.java0100664 0000000 0000000 00000007552 14245617503 023454 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.ssl; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import javax.net.ssl.SSLContext; /** * {@link javax.net.ssl.SSLContext} factory methods. * *

* Please note: the default Oracle JSSE implementation of * {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom) * SSLContext#init(KeyManager[], TrustManager[], SecureRandom)} * accepts multiple key and trust managers, however only only first matching type is ever used. * See for example: * * SSLContext.html#init * * @since 4.4 */ public final class SSLContexts { private SSLContexts() { // Do not allow utility class to be instantiated. } /** * Creates default factory based on the standard JSSE trust material * ({@code cacerts} file in the security properties directory). System properties * are not taken into consideration. * * @return the default SSL socket factory * @throws SSLInitializationException if NoSuchAlgorithmException or KeyManagementException * are thrown when invoking {@link SSLContext#getInstance(String)} */ public static SSLContext createDefault() throws SSLInitializationException { try { final SSLContext sslContext = SSLContext.getInstance(SSLContextBuilder.TLS); sslContext.init(null, null, null); return sslContext; } catch (final NoSuchAlgorithmException | KeyManagementException ex) { throw new SSLInitializationException(ex.getMessage(), ex); } } /** * Creates default SSL context based on system properties. This method obtains * default SSL context by calling {@code SSLContext.getInstance("Default")}. * Please note that {@code Default} algorithm is supported as of Java 6. * This method will fall back onto {@link #createDefault()} when * {@code Default} algorithm is not available. * * @return default system SSL context * @throws SSLInitializationException if {@link #createDefault()} throws it */ public static SSLContext createSystemDefault() throws SSLInitializationException { try { return SSLContext.getDefault(); } catch (final NoSuchAlgorithmException ex) { return createDefault(); } } /** * Creates custom SSL context. * * @return default system SSL context */ public static SSLContextBuilder custom() { return SSLContextBuilder.create(); } } httpcore5/src/main/java/org/apache/hc/core5/ssl/SSLInitializationException.java0100664 0000000 0000000 00000002721 14245617503 026504 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.ssl; public class SSLInitializationException extends IllegalStateException { private static final long serialVersionUID = -8243587425648536702L; public SSLInitializationException(final String message, final Throwable cause) { super(message, cause); } } httpcore5/src/main/java/org/apache/hc/core5/ssl/PrivateKeyStrategy.java0100664 0000000 0000000 00000003074 14245617503 025064 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.ssl; import java.util.Map; import javax.net.ssl.SSLParameters; /** * A strategy allowing for a choice of an alias during SSL authentication. * * @since 4.4 */ public interface PrivateKeyStrategy { /** * Determines what key material to use for SSL authentication. */ String chooseAlias(Map aliases, SSLParameters sslParameters); } httpcore5/src/main/java/org/apache/hc/core5/ssl/TrustStrategy.java0100664 0000000 0000000 00000004675 14245617503 024132 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.ssl; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** * A strategy to establish trustworthiness of certificates without consulting the trust manager * configured in the actual SSL context. This interface can be used to override the standard * JSSE certificate verification process. * * @since 4.4 */ public interface TrustStrategy { /** * Determines whether the certificate chain can be trusted without consulting the trust manager * configured in the actual SSL context. This method can be used to override the standard JSSE * certificate verification process. *

* Please note that, if this method returns {@code false}, the trust manager configured * in the actual SSL context can still clear the certificate as trusted. * * @param chain the peer certificate chain * @param authType the authentication type based on the client certificate * @return {@code true} if the certificate can be trusted without verification by * the trust manager, {@code false} otherwise. * @throws CertificateException thrown if the certificate is not trusted or invalid. */ boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException; } httpcore5/src/main/java/org/apache/hc/core5/ssl/PrivateKeyDetails.java0100664 0000000 0000000 00000003632 14245617503 024647 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.ssl; import java.security.cert.X509Certificate; import java.util.Arrays; import org.apache.hc.core5.util.Args; /** * Private key details. * * @since 4.4 */ public final class PrivateKeyDetails { private final String type; private final X509Certificate[] certChain; public PrivateKeyDetails(final String type, final X509Certificate[] certChain) { super(); this.type = Args.notNull(type, "Private key type"); this.certChain = certChain; } public String getType() { return type; } public X509Certificate[] getCertChain() { return certChain; } @Override public String toString() { return type + ':' + Arrays.toString(certChain); } } httpcore5/src/main/java/org/apache/hc/core5/ssl/SSLContextBuilder.java0100664 0000000 0000000 00000061337 14403631147 024575 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.ssl; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.net.URL; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Principal; import java.security.PrivateKey; import java.security.Provider; import java.security.SecureRandom; import java.security.Security; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509TrustManager; import org.apache.hc.core5.util.Args; /** * Builder for {@link javax.net.ssl.SSLContext} instances. *

* Please note: the default Oracle JSSE implementation of {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)} * accepts multiple key and trust managers, however only only first matching type is ever used. * See for example: * * SSLContext.html#init * * * @since 4.4 */ public class SSLContextBuilder { static final String TLS = "TLS"; private String protocol; private final Set keyManagers; private String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); private String keyStoreType = KeyStore.getDefaultType(); private final Set trustManagers; private String trustManagerFactoryAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); private SecureRandom secureRandom; private Provider provider; private Provider tsProvider; private Provider ksProvider; /** * An empty immutable {@code KeyManager} array. */ private static final KeyManager[] EMPTY_KEY_MANAGER_ARRAY = {}; /** * An empty immutable {@code TrustManager} array. */ private static final TrustManager[] EMPTY_TRUST_MANAGER_ARRAY = {}; public static SSLContextBuilder create() { return new SSLContextBuilder(); } public SSLContextBuilder() { this.keyManagers = new LinkedHashSet<>(); this.trustManagers = new LinkedHashSet<>(); } /** * Sets the SSLContext algorithm name. * * @param protocol * the SSLContext algorithm name of the requested protocol. See * the SSLContext section in the Java * Cryptography Architecture Standard Algorithm Name * Documentation for more information. * @return this builder * @see Java * Cryptography Architecture Standard Algorithm Name Documentation */ public SSLContextBuilder setProtocol(final String protocol) { this.protocol = protocol; return this; } public SSLContextBuilder setProvider(final Provider provider) { this.provider = provider; return this; } public SSLContextBuilder setProvider(final String name) { this.provider = Security.getProvider(name); return this; } /** * Sets the JCA provider to use for creating trust stores. * @param provider provider to use for creating trust stores. * @return this builder * @since 5.2 */ public SSLContextBuilder setTrustStoreProvider(final Provider provider) { this.tsProvider = provider; return this; } /** * Sets the JCA provider name to use for creating trust stores. * @param name Name of the provider to use for creating trust stores, the provider must be registered with the JCA. * @return this builder * @since 5.2 */ public SSLContextBuilder setTrustStoreProvider(final String name) throws NoSuchProviderException { this.tsProvider = requireNonNullProvider(name); return this; } /** * Sets the JCA provider to use for creating key stores. * @param provider provider to use for creating key stores. * @return this builder * @since 5.2 */ public SSLContextBuilder setKeyStoreProvider(final Provider provider) { this.ksProvider = provider; return this; } /** * Sets the JCA provider name to use for creating key stores. * @param name Name of the provider to use for creating key stores, the provider must be registered with the JCA. * @return this builder * @since 5.2 */ public SSLContextBuilder setKeyStoreProvider(final String name) throws NoSuchProviderException { this.ksProvider = requireNonNullProvider(name); return this; } /** * Sets the key store type. * * @param keyStoreType * the SSLkey store type. See * the KeyStore section in the Java * Cryptography Architecture Standard Algorithm Name * Documentation for more information. * @return this builder * @see Java * Cryptography Architecture Standard Algorithm Name Documentation * @since 4.4.7 */ public SSLContextBuilder setKeyStoreType(final String keyStoreType) { this.keyStoreType = keyStoreType; return this; } /** * Sets the key manager factory algorithm name. * * @param keyManagerFactoryAlgorithm * the key manager factory algorithm name of the requested protocol. See * the KeyManagerFactory section in the Java * Cryptography Architecture Standard Algorithm Name * Documentation for more information. * @return this builder * @see Java * Cryptography Architecture Standard Algorithm Name Documentation * @since 4.4.7 */ public SSLContextBuilder setKeyManagerFactoryAlgorithm(final String keyManagerFactoryAlgorithm) { this.keyManagerFactoryAlgorithm = keyManagerFactoryAlgorithm; return this; } /** * Sets the trust manager factory algorithm name. * * @param trustManagerFactoryAlgorithm * the trust manager algorithm name of the requested protocol. See * the TrustManagerFactory section in the Java * Cryptography Architecture Standard Algorithm Name * Documentation for more information. * @return this builder * @see Java * Cryptography Architecture Standard Algorithm Name Documentation * @since 4.4.7 */ public SSLContextBuilder setTrustManagerFactoryAlgorithm(final String trustManagerFactoryAlgorithm) { this.trustManagerFactoryAlgorithm = trustManagerFactoryAlgorithm; return this; } public SSLContextBuilder setSecureRandom(final SecureRandom secureRandom) { this.secureRandom = secureRandom; return this; } public SSLContextBuilder loadTrustMaterial( final KeyStore trustStore, final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException { final String alg = trustManagerFactoryAlgorithm == null ? TrustManagerFactory.getDefaultAlgorithm() : trustManagerFactoryAlgorithm; final TrustManagerFactory tmFactory = tsProvider == null ? TrustManagerFactory.getInstance(alg) : TrustManagerFactory.getInstance(alg, tsProvider); tmFactory.init(trustStore); final TrustManager[] tms = tmFactory.getTrustManagers(); if (tms != null) { if (trustStrategy != null) { for (int i = 0; i < tms.length; i++) { final TrustManager tm = tms[i]; if (tm instanceof X509TrustManager) { tms[i] = new TrustManagerDelegate((X509TrustManager) tm, trustStrategy); } } } Collections.addAll(this.trustManagers, tms); } return this; } /** * @since 5.2 */ public SSLContextBuilder loadTrustMaterial( final Path file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { return loadTrustMaterial(file, null); } /** * @since 5.2 */ public SSLContextBuilder loadTrustMaterial( final Path file, final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { return loadTrustMaterial(file, storePassword, null); } /** * @since 5.2 */ public SSLContextBuilder loadTrustMaterial( final Path file, final char[] storePassword, final TrustStrategy trustStrategy, final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { Args.notNull(file, "Truststore file"); return loadTrustMaterial(loadKeyStore(file, storePassword, openOptions), trustStrategy); } public SSLContextBuilder loadTrustMaterial( final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException { return loadTrustMaterial(null, trustStrategy); } public SSLContextBuilder loadTrustMaterial( final File file, final char[] storePassword, final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { Args.notNull(file, "Truststore file"); return loadTrustMaterial(file.toPath(), storePassword, trustStrategy); } public SSLContextBuilder loadTrustMaterial( final File file, final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { return loadTrustMaterial(file, storePassword, null); } public SSLContextBuilder loadTrustMaterial( final File file) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { return loadTrustMaterial(file, null); } public SSLContextBuilder loadTrustMaterial( final URL url, final char[] storePassword, final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { Args.notNull(url, "Truststore URL"); return loadTrustMaterial(loadKeyStore(url, storePassword), trustStrategy); } public SSLContextBuilder loadTrustMaterial( final URL url, final char[] storePassword) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { return loadTrustMaterial(url, storePassword, null); } public SSLContextBuilder loadKeyMaterial( final KeyStore keyStore, final char[] keyPassword, final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException { final String alg = keyManagerFactoryAlgorithm == null ? KeyManagerFactory.getDefaultAlgorithm() : keyManagerFactoryAlgorithm; final KeyManagerFactory kmFactory = ksProvider == null ? KeyManagerFactory.getInstance(alg) : KeyManagerFactory.getInstance(alg, ksProvider); kmFactory.init(keyStore, keyPassword); final KeyManager[] kms = kmFactory.getKeyManagers(); if (kms != null) { if (aliasStrategy != null) { for (int i = 0; i < kms.length; i++) { final KeyManager km = kms[i]; if (km instanceof X509ExtendedKeyManager) { kms[i] = new KeyManagerDelegate((X509ExtendedKeyManager) km, aliasStrategy); } } } Collections.addAll(keyManagers, kms); } return this; } /** * @since 5.2 */ public SSLContextBuilder loadKeyMaterial( final Path file, final char[] storePassword, final char[] keyPassword, final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException { return loadKeyMaterial(file, storePassword, keyPassword, null, openOptions); } /** * @since 5.2 */ public SSLContextBuilder loadKeyMaterial( final Path file, final char[] storePassword, final char[] keyPassword, final PrivateKeyStrategy aliasStrategy, final OpenOption... openOptions) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException { Args.notNull(file, "Keystore file"); return loadKeyMaterial(loadKeyStore(file, storePassword, openOptions), keyPassword, aliasStrategy); } public SSLContextBuilder loadKeyMaterial( final KeyStore keyStore, final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException { return loadKeyMaterial(keyStore, keyPassword, null); } public SSLContextBuilder loadKeyMaterial( final File file, final char[] storePassword, final char[] keyPassword, final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException { Args.notNull(file, "Keystore file"); return loadKeyMaterial(file.toPath(), storePassword, keyPassword, aliasStrategy); } public SSLContextBuilder loadKeyMaterial( final File file, final char[] storePassword, final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException { return loadKeyMaterial(file, storePassword, keyPassword, null); } public SSLContextBuilder loadKeyMaterial( final URL url, final char[] storePassword, final char[] keyPassword, final PrivateKeyStrategy aliasStrategy) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException { Args.notNull(url, "Keystore URL"); return loadKeyMaterial(loadKeyStore(url, storePassword), keyPassword, aliasStrategy); } public SSLContextBuilder loadKeyMaterial( final URL url, final char[] storePassword, final char[] keyPassword) throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException { return loadKeyMaterial(url, storePassword, keyPassword, null); } protected void initSSLContext( final SSLContext sslContext, final Collection keyManagers, final Collection trustManagers, final SecureRandom secureRandom) throws KeyManagementException { sslContext.init( !keyManagers.isEmpty() ? keyManagers.toArray(EMPTY_KEY_MANAGER_ARRAY) : null, !trustManagers.isEmpty() ? trustManagers.toArray(EMPTY_TRUST_MANAGER_ARRAY) : null, secureRandom); } private KeyStore loadKeyStore(final Path file, final char[] password, final OpenOption... openOptions) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { final KeyStore keyStore = KeyStore.getInstance(keyStoreType); try (final InputStream inputStream = Files.newInputStream(file, openOptions)) { keyStore.load(inputStream, password); } return keyStore; } private KeyStore loadKeyStore(final URL url, final char[] password) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { final KeyStore keyStore = KeyStore.getInstance(keyStoreType); try (final InputStream inputStream = url.openStream()) { keyStore.load(inputStream, password); } return keyStore; } public SSLContext build() throws NoSuchAlgorithmException, KeyManagementException { final SSLContext sslContext; final String protocolStr = this.protocol != null ? this.protocol : TLS; if (this.provider != null) { sslContext = SSLContext.getInstance(protocolStr, this.provider); } else { sslContext = SSLContext.getInstance(protocolStr); } initSSLContext(sslContext, keyManagers, trustManagers, secureRandom); return sslContext; } static class TrustManagerDelegate implements X509TrustManager { private final X509TrustManager trustManager; private final TrustStrategy trustStrategy; TrustManagerDelegate(final X509TrustManager trustManager, final TrustStrategy trustStrategy) { this.trustManager = trustManager; this.trustStrategy = trustStrategy; } @Override public void checkClientTrusted( final X509Certificate[] chain, final String authType) throws CertificateException { this.trustManager.checkClientTrusted(chain, authType); } @Override public void checkServerTrusted( final X509Certificate[] chain, final String authType) throws CertificateException { if (!this.trustStrategy.isTrusted(chain, authType)) { this.trustManager.checkServerTrusted(chain, authType); } } @Override public X509Certificate[] getAcceptedIssuers() { return this.trustManager.getAcceptedIssuers(); } } static class KeyManagerDelegate extends X509ExtendedKeyManager { private final X509ExtendedKeyManager keyManager; private final PrivateKeyStrategy aliasStrategy; KeyManagerDelegate(final X509ExtendedKeyManager keyManager, final PrivateKeyStrategy aliasStrategy) { this.keyManager = keyManager; this.aliasStrategy = aliasStrategy; } @Override public String[] getClientAliases( final String keyType, final Principal[] issuers) { return this.keyManager.getClientAliases(keyType, issuers); } public Map getClientAliasMap( final String[] keyTypes, final Principal[] issuers) { final Map validAliases = new HashMap<>(); for (final String keyType: keyTypes) { putPrivateKeyDetails(validAliases, keyType, this.keyManager.getClientAliases(keyType, issuers)); } return validAliases; } public Map getServerAliasMap( final String keyType, final Principal[] issuers) { final Map validAliases = new HashMap<>(); putPrivateKeyDetails(validAliases, keyType, this.keyManager.getServerAliases(keyType, issuers)); return validAliases; } private void putPrivateKeyDetails(final Map validAliases, final String keyType, final String[] aliases) { if (aliases != null) { for (final String alias: aliases) { validAliases.put(alias, new PrivateKeyDetails(keyType, this.keyManager.getCertificateChain(alias))); } } } @Override public String chooseClientAlias( final String[] keyTypes, final Principal[] issuers, final Socket socket) { final Map validAliases = getClientAliasMap(keyTypes, issuers); return this.aliasStrategy.chooseAlias(validAliases, socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null); } @Override public String[] getServerAliases( final String keyType, final Principal[] issuers) { return this.keyManager.getServerAliases(keyType, issuers); } @Override public String chooseServerAlias( final String keyType, final Principal[] issuers, final Socket socket) { final Map validAliases = getServerAliasMap(keyType, issuers); return this.aliasStrategy.chooseAlias(validAliases, socket instanceof SSLSocket ? ((SSLSocket) socket).getSSLParameters() : null); } @Override public X509Certificate[] getCertificateChain(final String alias) { return this.keyManager.getCertificateChain(alias); } @Override public PrivateKey getPrivateKey(final String alias) { return this.keyManager.getPrivateKey(alias); } @Override public String chooseEngineClientAlias( final String[] keyTypes, final Principal[] issuers, final SSLEngine sslEngine) { final Map validAliases = getClientAliasMap(keyTypes, issuers); return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters()); } @Override public String chooseEngineServerAlias( final String keyType, final Principal[] issuers, final SSLEngine sslEngine) { final Map validAliases = getServerAliasMap(keyType, issuers); return this.aliasStrategy.chooseAlias(validAliases, sslEngine.getSSLParameters()); } } private Provider requireNonNullProvider(final String name) throws NoSuchProviderException { final Provider provider = Security.getProvider(name); if (provider == null) { throw new NoSuchProviderException(name); } return provider; } @Override public String toString() { return "[provider=" + provider + ", protocol=" + protocol + ", keyStoreType=" + keyStoreType + ", keyManagerFactoryAlgorithm=" + keyManagerFactoryAlgorithm + ", keyManagers=" + keyManagers + ", trustManagerFactoryAlgorithm=" + trustManagerFactoryAlgorithm + ", trustManagers=" + trustManagers + ", secureRandom=" + secureRandom + "]"; } } httpcore5/src/main/java/org/apache/hc/core5/http/0040775 0000000 0000000 00000000000 14435411723 020565 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/Message.java0100664 0000000 0000000 00000003625 14245617503 023022 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.apache.hc.core5.util.Args; /** * Generic message consisting of a message head and a message body. * * @param message head type. * @param message body type. * * @since 5.0 */ public final class Message { private final H head; private final B body; public Message(final H head, final B body) { this.head = Args.notNull(head, "Message head"); this.body = body; } public H getHead() { return head; } public B getBody() { return body; } @Override public String toString() { return "[" + "head=" + head + ", body=" + body + ']'; } } httpcore5/src/main/java/org/apache/hc/core5/http/package-info.java0100664 0000000 0000000 00000003572 14245617503 023763 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core HTTP transport component APIs. *

* These deal with the fundamental things required for using the * HTTP protocol, such as representing a * {@link org.apache.hc.core5.http.HttpMessage message} including it's * {@link org.apache.hc.core5.http.Header headers} and optional * {@link org.apache.hc.core5.http.HttpEntity entity}, and * {@link org.apache.hc.core5.http.HttpConnection connections} * over which messages are sent. In order to prepare messages * before sending or after receiving, there are interceptors for * {@link org.apache.hc.core5.http.HttpRequestInterceptor requests} and * {@link org.apache.hc.core5.http.HttpResponseInterceptor responses}. *

*/ package org.apache.hc.core5.http; httpcore5/src/main/java/org/apache/hc/core5/http/ProtocolVersion.java0100664 0000000 0000000 00000020512 14245617503 024577 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.Serializable; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Represents a protocol version. The "major.minor" numbering * scheme is used to indicate versions of the protocol. *

* This class defines a protocol version as a combination of * protocol name, major version number, and minor version number. * Note that {@link #equals} and {@link #hashCode} are defined as * final here, they cannot be overridden in derived classes. *

* * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class ProtocolVersion implements Serializable { private static final long serialVersionUID = 8950662842175091068L; /** Name of the protocol. */ private final String protocol; /** Major version number of the protocol */ private final int major; /** Minor version number of the protocol */ private final int minor; /** * Create a protocol version designator. * * @param protocol the name of the protocol, for example "HTTP" * @param major the major version number of the protocol * @param minor the minor version number of the protocol */ public ProtocolVersion(final String protocol, final int major, final int minor) { this.protocol = Args.notNull(protocol, "Protocol name"); this.major = Args.notNegative(major, "Protocol minor version"); this.minor = Args.notNegative(minor, "Protocol minor version"); } /** * Returns the name of the protocol. * * @return the protocol name */ public final String getProtocol() { return protocol; } /** * Returns the major version number of the protocol. * * @return the major version number. */ public final int getMajor() { return major; } /** * Returns the minor version number of the HTTP protocol. * * @return the minor version number. */ public final int getMinor() { return minor; } /** * Obtains a hash code consistent with {@link #equals}. * * @return the hashcode of this protocol version */ @Override public final int hashCode() { return this.protocol.hashCode() ^ (this.major * 100000) ^ this.minor; } /** * Checks whether this instance has the same major and minor version as the arguments. * * @param major the major version to check. * @param minor the minor version to check. * @return whether this instance has the same major and minor version as the arguments. * @since 5.0 */ public final boolean equals(final int major, final int minor) { return this.major == major && this.minor == minor; } /** * Checks equality of this protocol version with an object. * The object is equal if it is a protocol version with the same * protocol name, major version number, and minor version number. * The specific class of the object is not relevant, * instances of derived classes with identical attributes are * equal to instances of the base class and vice versa. * * @param obj the object to compare with * * @return {@code true} if the argument is the same protocol version, * {@code false} otherwise */ @Override public final boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof ProtocolVersion)) { return false; } final ProtocolVersion that = (ProtocolVersion) obj; return (this.protocol.equals(that.protocol) && (this.major == that.major) && (this.minor == that.minor)); } /** * Formats this protocol version as a string. * * @return a protocol version string, like "HTTP/1.1" * @since 5.0 */ public String format() { final StringBuilder buffer = new StringBuilder(); buffer.append(this.protocol); buffer.append('/'); buffer.append(this.major); buffer.append('.'); buffer.append(this.minor); return buffer.toString(); } /** * Checks whether this protocol can be compared to another one. * Only protocol versions with the same protocol name can be * {@link #compareToVersion compared}. * * @param that the protocol version to consider * * @return {@code true} if {@link #compareToVersion compareToVersion} * can be called with the argument, {@code false} otherwise */ public boolean isComparable(final ProtocolVersion that) { return (that != null) && this.protocol.equals(that.protocol); } /** * Compares this protocol version with another one. * Only protocol versions with the same protocol name can be compared. * This method does not define a total ordering, as it would be * required for {@link java.lang.Comparable}. * * @param that the protocol version to compare with * * @return a negative integer, zero, or a positive integer * as this version is less than, equal to, or greater than * the argument version. * * @throws IllegalArgumentException * if the argument has a different protocol name than this object, * or if the argument is {@code null} */ public int compareToVersion(final ProtocolVersion that) { Args.notNull(that, "Protocol version"); Args.check(this.protocol.equals(that.protocol), "Versions for different protocols cannot be compared: %s %s", this, that); int delta = getMajor() - that.getMajor(); if (delta == 0) { delta = getMinor() - that.getMinor(); } return delta; } /** * Tests if this protocol version is greater or equal to the given one. * * @param version the version against which to check this version * * @return {@code true} if this protocol version is * {@link #isComparable comparable} to the argument * and {@link #compareToVersion compares} as greater or equal, * {@code false} otherwise */ public final boolean greaterEquals(final ProtocolVersion version) { return isComparable(version) && (compareToVersion(version) >= 0); } /** * Tests if this protocol version is less or equal to the given one. * * @param version the version against which to check this version * * @return {@code true} if this protocol version is * {@link #isComparable comparable} to the argument * and {@link #compareToVersion compares} as less or equal, * {@code false} otherwise */ public final boolean lessEquals(final ProtocolVersion version) { return isComparable(version) && (compareToVersion(version) <= 0); } /** * Converts this protocol version to a string. * * @return a protocol version string, like "HTTP/1.1" */ @Override public String toString() { return format(); } } httpcore5/src/main/java/org/apache/hc/core5/http/config/0040775 0000000 0000000 00000000000 14435411677 022042 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/config/package-info.java0100664 0000000 0000000 00000002356 14245617503 025227 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core configuration APIs. */ package org.apache.hc.core5.http.config; httpcore5/src/main/java/org/apache/hc/core5/http/config/RegistryBuilder.java0100664 0000000 0000000 00000004037 14403631147 026014 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.config; import java.util.HashMap; import java.util.Map; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * Builder for {@link Registry} instances. * * @since 4.3 */ public final class RegistryBuilder { private final Map items; public static RegistryBuilder create() { return new RegistryBuilder<>(); } RegistryBuilder() { super(); this.items = new HashMap<>(); } public RegistryBuilder register(final String id, final I item) { Args.notEmpty(id, "ID"); Args.notNull(item, "Item"); items.put(TextUtils.toLowerCase(id), item); return this; } public Registry build() { return new Registry<>(items); } @Override public String toString() { return items.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/config/Registry.java0100664 0000000 0000000 00000003773 14403631147 024513 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.config; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.TextUtils; /** * Generic registry of items keyed by low-case string ID. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.SAFE) public final class Registry implements Lookup { private final Map map; Registry(final Map map) { super(); this.map = new ConcurrentHashMap<>(map); } @Override public I lookup(final String key) { if (key == null) { return null; } return map.get(TextUtils.toLowerCase(key)); } @Override public String toString() { return map.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/config/Lookup.java0100664 0000000 0000000 00000002512 14245617503 024146 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.config; /** * Generic lookup by low-case string ID. * * @since 4.3 */ public interface Lookup { I lookup(String name); } httpcore5/src/main/java/org/apache/hc/core5/http/config/CharCodingConfig.java0100664 0000000 0000000 00000011174 14435411677 026035 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.config; import java.nio.charset.Charset; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * HTTP/1.1 char coding configuration. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class CharCodingConfig { public static final CharCodingConfig DEFAULT = new Builder().build(); private final Charset charset; private final CodingErrorAction malformedInputAction; private final CodingErrorAction unmappableInputAction; CharCodingConfig( final Charset charset, final CodingErrorAction malformedInputAction, final CodingErrorAction unmappableInputAction) { super(); this.charset = charset; this.malformedInputAction = malformedInputAction; this.unmappableInputAction = unmappableInputAction; } public Charset getCharset() { return charset; } public CodingErrorAction getMalformedInputAction() { return malformedInputAction; } public CodingErrorAction getUnmappableInputAction() { return unmappableInputAction; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("[charset=").append(this.charset) .append(", malformedInputAction=").append(this.malformedInputAction) .append(", unmappableInputAction=").append(this.unmappableInputAction) .append("]"); return builder.toString(); } public static CharCodingConfig.Builder custom() { return new Builder(); } public static CharCodingConfig.Builder copy(final CharCodingConfig config) { Args.notNull(config, "Config"); return new Builder() .setCharset(config.getCharset()) .setMalformedInputAction(config.getMalformedInputAction()) .setUnmappableInputAction(config.getUnmappableInputAction()); } public static class Builder { private Charset charset; private CodingErrorAction malformedInputAction; private CodingErrorAction unmappableInputAction; Builder() { } public Builder setCharset(final Charset charset) { this.charset = charset; return this; } public Builder setMalformedInputAction(final CodingErrorAction malformedInputAction) { this.malformedInputAction = malformedInputAction; if (malformedInputAction != null && this.charset == null) { this.charset = StandardCharsets.US_ASCII; } return this; } public Builder setUnmappableInputAction(final CodingErrorAction unmappableInputAction) { this.unmappableInputAction = unmappableInputAction; if (unmappableInputAction != null && this.charset == null) { this.charset = StandardCharsets.US_ASCII; } return this; } public CharCodingConfig build() { Charset charsetCopy = charset; if (charsetCopy == null && (malformedInputAction != null || unmappableInputAction != null)) { charsetCopy = StandardCharsets.US_ASCII; } return new CharCodingConfig(charsetCopy, malformedInputAction, unmappableInputAction); } } } httpcore5/src/main/java/org/apache/hc/core5/http/config/Http1Config.java0100664 0000000 0000000 00000016165 14403631147 025030 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.config; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * HTTP/1.1 protocol parameters. *

* Please note that line length is defined in bytes and not characters. * This is only relevant however when using non-standard HTTP charsets * for protocol elements such as UTF-8. *

* * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class Http1Config { public static final Http1Config DEFAULT = new Builder().build(); private final int bufferSize; private final int chunkSizeHint; private final Timeout waitForContinueTimeout; private final int maxLineLength; private final int maxHeaderCount; private final int maxEmptyLineCount; private final int initialWindowSize; Http1Config(final int bufferSize, final int chunkSizeHint, final Timeout waitForContinueTimeout, final int maxLineLength, final int maxHeaderCount, final int maxEmptyLineCount, final int initialWindowSize) { super(); this.bufferSize = bufferSize; this.chunkSizeHint = chunkSizeHint; this.waitForContinueTimeout = waitForContinueTimeout; this.maxLineLength = maxLineLength; this.maxHeaderCount = maxHeaderCount; this.maxEmptyLineCount = maxEmptyLineCount; this.initialWindowSize = initialWindowSize; } public int getBufferSize() { return bufferSize; } public int getChunkSizeHint() { return chunkSizeHint; } public Timeout getWaitForContinueTimeout() { return waitForContinueTimeout; } public int getMaxLineLength() { return maxLineLength; } public int getMaxHeaderCount() { return maxHeaderCount; } public int getMaxEmptyLineCount() { return this.maxEmptyLineCount; } public int getInitialWindowSize() { return initialWindowSize; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("[bufferSize=").append(bufferSize) .append(", chunkSizeHint=").append(chunkSizeHint) .append(", waitForContinueTimeout=").append(waitForContinueTimeout) .append(", maxLineLength=").append(maxLineLength) .append(", maxHeaderCount=").append(maxHeaderCount) .append(", maxEmptyLineCount=").append(maxEmptyLineCount) .append(", initialWindowSize=").append(initialWindowSize) .append("]"); return builder.toString(); } public static Http1Config.Builder custom() { return new Builder(); } public static Http1Config.Builder copy(final Http1Config config) { Args.notNull(config, "Config"); return new Builder() .setBufferSize(config.getBufferSize()) .setChunkSizeHint(config.getChunkSizeHint()) .setWaitForContinueTimeout(config.getWaitForContinueTimeout()) .setMaxHeaderCount(config.getMaxHeaderCount()) .setMaxLineLength(config.getMaxLineLength()) .setMaxEmptyLineCount(config.getMaxEmptyLineCount()) .setInitialWindowSize(config.getInitialWindowSize()); } private static final int INIT_WINDOW_SIZE = 65535; private static final int INIT_BUF_SIZE = 8192; private static final Timeout INIT_WAIT_FOR_CONTINUE = Timeout.ofSeconds(3); private static final int INIT_BUF_CHUNK = -1; private static final int INIT_MAX_HEADER_COUNT = -1; private static final int INIT_MAX_LINE_LENGTH = -1; private static final int INIT_MAX_EMPTY_LINE_COUNT = 10; public static class Builder { private int bufferSize; private int chunkSizeHint; private Timeout waitForContinueTimeout; private int maxLineLength; private int maxHeaderCount; private int maxEmptyLineCount; private int initialWindowSize; Builder() { this.bufferSize = INIT_BUF_SIZE; this.chunkSizeHint = INIT_BUF_CHUNK; this.waitForContinueTimeout = INIT_WAIT_FOR_CONTINUE; this.maxLineLength = INIT_MAX_LINE_LENGTH; this.maxHeaderCount = INIT_MAX_HEADER_COUNT; this.maxEmptyLineCount = INIT_MAX_EMPTY_LINE_COUNT; this.initialWindowSize = INIT_WINDOW_SIZE; } public Builder setBufferSize(final int bufferSize) { this.bufferSize = bufferSize; return this; } public Builder setChunkSizeHint(final int chunkSizeHint) { this.chunkSizeHint = chunkSizeHint; return this; } public Builder setWaitForContinueTimeout(final Timeout waitForContinueTimeout) { this.waitForContinueTimeout = waitForContinueTimeout; return this; } public Builder setMaxLineLength(final int maxLineLength) { this.maxLineLength = maxLineLength; return this; } public Builder setMaxHeaderCount(final int maxHeaderCount) { this.maxHeaderCount = maxHeaderCount; return this; } public Builder setMaxEmptyLineCount(final int maxEmptyLineCount) { this.maxEmptyLineCount = maxEmptyLineCount; return this; } public Builder setInitialWindowSize(final int initialWindowSize) { Args.positive(initialWindowSize, "Initial window size"); this.initialWindowSize = initialWindowSize; return this; } public Http1Config build() { return new Http1Config( bufferSize, chunkSizeHint, waitForContinueTimeout, maxLineLength, maxHeaderCount, maxEmptyLineCount, initialWindowSize); } } } httpcore5/src/main/java/org/apache/hc/core5/http/config/NamedElementChain.java0100664 0000000 0000000 00000012705 14245617503 026203 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.config; import org.apache.hc.core5.util.Args; /** * Chain of doubly linked elements. *

* This implementation makes no attempts to ensure uniqueness of element names. * * @param */ public class NamedElementChain { private final Node master; private int size; public NamedElementChain() { this.master = new Node("master", null); this.master.previous = this.master; this.master.next = this.master; this.size = 0; } public Node getFirst() { return master.next != master ? master.next : null; } public Node getLast() { return master.previous != master ? master.previous : null; } public Node addFirst(final E value, final String name) { Args.notBlank(name, "Name"); Args.notNull(value, "Value"); final Node newNode = new Node(name, value); final Node oldNode = master.next; master.next = newNode; newNode.previous = master; newNode.next = oldNode; oldNode.previous = newNode; size++; return newNode; } public Node addLast(final E value, final String name) { Args.notBlank(name, "Name"); Args.notNull(value, "Value"); final Node newNode = new Node(name, value); final Node oldNode = master.previous; master.previous = newNode; newNode.previous = oldNode; newNode.next = master; oldNode.next = newNode; size++; return newNode; } public Node find(final String name) { Args.notBlank(name, "Name"); return doFind(name); } private Node doFind(final String name) { Node current = master.next; while (current != master) { if (name.equals(current.name)) { return current; } current = current.next; } return null; } public Node addBefore(final String existing, final E value, final String name) { Args.notBlank(name, "Name"); Args.notNull(value, "Value"); final Node current = doFind(existing); if (current == null) { return null; } final Node newNode = new Node(name, value); final Node previousNode = current.previous; previousNode.next = newNode; newNode.previous = previousNode; newNode.next = current; current.previous = newNode; size++; return newNode; } public Node addAfter(final String existing, final E value, final String name) { Args.notBlank(name, "Name"); Args.notNull(value, "Value"); final Node current = doFind(existing); if (current == null) { return null; } final Node newNode = new Node(name, value); final Node nextNode = current.next; current.next = newNode; newNode.previous = current; newNode.next = nextNode; nextNode.previous = newNode; size++; return newNode; } public boolean remove(final String name) { final Node node = doFind(name); if (node == null) { return false; } node.previous.next = node.next; node.next.previous = node.previous; node.previous = null; node.next = null; size--; return true; } public boolean replace(final String existing, final E value) { final Node node = doFind(existing); if (node == null) { return false; } node.value = value; return true; } public int getSize() { return size; } public class Node { private final String name; private E value; private Node previous; private Node next; Node(final String name, final E value) { this.name = name; this.value = value; } public String getName() { return name; } public E getValue() { return value; } public Node getPrevious() { return previous != master ? previous : null; } public Node getNext() { return next != master ? next: null; } @Override public String toString() { return name + ": " + value; } } }httpcore5/src/main/java/org/apache/hc/core5/http/HttpRequestInterceptor.java0100664 0000000 0000000 00000005641 14245617503 026145 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * HTTP protocol interceptor is a routine that implements a specific aspect of * the HTTP protocol. Usually protocol interceptors are expected to act upon * one specific header or a group of related headers of the incoming message * or populate the outgoing message with one specific header or a group of * related headers. *

* Protocol Interceptors can also manipulate content entities enclosed with messages. * Usually this is accomplished by using the 'Decorator' pattern where a wrapper * entity class is used to decorate the original entity. *

* Protocol interceptors must be implemented as thread-safe. Similarly to * servlets, protocol interceptors should not use instance variables unless * access to those variables is synchronized. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpRequestInterceptor { /** * Processes a request. * On the client side, this step is performed before the request is * sent to the server. On the server side, this step is performed * on incoming messages before the message body is evaluated. * * @param request the request to process * @param entity the request entity details or {@code null} if not available * @param context the context for the request * * @throws HttpException in case of an HTTP protocol violation * @throws IOException in case of an I/O error */ void process(HttpRequest request, EntityDetails entity, HttpContext context) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/HeaderElements.java0100664 0000000 0000000 00000003162 14245617503 024317 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Constants for frequently used Header elements. * * @since 5.0 */ public final class HeaderElements { private HeaderElements() { } public static final String CHUNKED_ENCODING = "chunked"; public static final String CLOSE = "close"; public static final String KEEP_ALIVE = "keep-alive"; public static final String UPGRADE = "upgrade"; public static final String CONTINUE = "100-continue"; } httpcore5/src/main/java/org/apache/hc/core5/http/Method.java0100664 0000000 0000000 00000006004 14245617503 022650 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.util.Locale; import org.apache.hc.core5.util.Args; /** * Common HTTP methods defined by the HTTP spec. * * @since 5.0 */ public enum Method { GET(true, true), HEAD(true, true), POST(false, false), PUT(false, true), DELETE(false, true), CONNECT(false, false), TRACE(true, true), OPTIONS(true, true), PATCH(false, false); private final boolean safe; private final boolean idempotent; Method(final boolean safe, final boolean idempotent) { this.safe = safe; this.idempotent = idempotent; } public boolean isSafe() { return safe; } public boolean isIdempotent() { return idempotent; } public static boolean isSafe(final String value) { if (value == null) { return false; } try { return normalizedValueOf(value).safe; } catch (final IllegalArgumentException ex) { return false; } } public static boolean isIdempotent(final String value) { if (value == null) { return false; } try { return normalizedValueOf(value).idempotent; } catch (final IllegalArgumentException ex) { return false; } } /** * Returns the Method for a normalized {@code value} of a method name. * * @param method A method name like {@code "delete"}, {@code "DELETE"}, or any mixed-case variant. * @return the Method for the given method name. */ public static Method normalizedValueOf(final String method) { return valueOf(Args.notNull(method, "method").toUpperCase(Locale.ROOT)); } public boolean isSame(final String value) { if (value == null) { return false; } return name().equalsIgnoreCase(value); } } httpcore5/src/main/java/org/apache/hc/core5/http/ContentType.java0100664 0000000 0000000 00000046060 14435411723 023707 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.Serializable; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.message.BasicHeaderValueFormatter; import org.apache.hc.core5.http.message.BasicHeaderValueParser; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.http.message.ParserCursor; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; import org.apache.hc.core5.util.TextUtils; /** * Content type information consisting of a MIME type and an optional charset. *

* This class makes no attempts to verify validity of the MIME type. * The input parameters of the {@link #create(String, String)} method, however, may not * contain characters {@code <">, <;>, <,>} reserved by the HTTP specification. * * @since 4.2 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class ContentType implements Serializable { private static final long serialVersionUID = -7768694718232371896L; /** * Param that represent {@code charset} constant. */ private static final String CHARSET = "charset"; // constants public static final ContentType APPLICATION_ATOM_XML = create( "application/atom+xml", StandardCharsets.UTF_8); public static final ContentType APPLICATION_FORM_URLENCODED = create( "application/x-www-form-urlencoded", StandardCharsets.ISO_8859_1); public static final ContentType APPLICATION_JSON = create( "application/json", StandardCharsets.UTF_8); /** * Public constant media type for {@code application/x-ndjson}. * @since 5.1 */ public static final ContentType APPLICATION_NDJSON = create( "application/x-ndjson", StandardCharsets.UTF_8); public static final ContentType APPLICATION_OCTET_STREAM = create( "application/octet-stream", (Charset) null); /** * Public constant media type for {@code application/pdf}. * @since 5.1 */ public static final ContentType APPLICATION_PDF = create( "application/pdf", StandardCharsets.UTF_8); public static final ContentType APPLICATION_SOAP_XML = create( "application/soap+xml", StandardCharsets.UTF_8); public static final ContentType APPLICATION_SVG_XML = create( "application/svg+xml", StandardCharsets.UTF_8); public static final ContentType APPLICATION_XHTML_XML = create( "application/xhtml+xml", StandardCharsets.UTF_8); public static final ContentType APPLICATION_XML = create( "application/xml", StandardCharsets.UTF_8); /** * Public constant media type for {@code application/problem+json}. * @see Problem Details for HTTP APIs, 6.1. application/problem+json * @since 5.1 */ public static final ContentType APPLICATION_PROBLEM_JSON = create( "application/problem+json", StandardCharsets.UTF_8); /** * Public constant media type for {@code application/problem+xml}. * @see Problem Details for HTTP APIs, 6.2. application/problem+xml * @since 5.1 */ public static final ContentType APPLICATION_PROBLEM_XML = create( "application/problem+xml", StandardCharsets.UTF_8); /** * Public constant media type for {@code application/rss+xml}. * @since 5.1 */ public static final ContentType APPLICATION_RSS_XML = create( "application/rss+xml", StandardCharsets.UTF_8); public static final ContentType IMAGE_BMP = create( "image/bmp"); public static final ContentType IMAGE_GIF = create( "image/gif"); public static final ContentType IMAGE_JPEG = create( "image/jpeg"); public static final ContentType IMAGE_PNG = create( "image/png"); public static final ContentType IMAGE_SVG = create( "image/svg+xml"); public static final ContentType IMAGE_TIFF = create( "image/tiff"); public static final ContentType IMAGE_WEBP = create( "image/webp"); public static final ContentType MULTIPART_FORM_DATA = create( "multipart/form-data", StandardCharsets.ISO_8859_1); /** * Public constant media type for {@code multipart/mixed}. * @since 5.1 */ public static final ContentType MULTIPART_MIXED = create( "multipart/mixed", StandardCharsets.ISO_8859_1); /** * Public constant media type for {@code multipart/related}. * @since 5.1 */ public static final ContentType MULTIPART_RELATED = create( "multipart/related", StandardCharsets.ISO_8859_1); public static final ContentType TEXT_HTML = create( "text/html", StandardCharsets.ISO_8859_1); /** * Public constant media type for {@code text/markdown}. * @since 5.1 */ public static final ContentType TEXT_MARKDOWN = create( "text/markdown", StandardCharsets.UTF_8); public static final ContentType TEXT_PLAIN = create( "text/plain", StandardCharsets.ISO_8859_1); public static final ContentType TEXT_XML = create( "text/xml", StandardCharsets.UTF_8); /** * Public constant media type for {@code text/event-stream}. * @see Server-Sent Events W3C recommendation * @since 5.1 */ public static final ContentType TEXT_EVENT_STREAM = create( "text/event-stream", StandardCharsets.UTF_8); public static final ContentType WILDCARD = create( "*/*", (Charset) null); /** * An empty immutable {@code NameValuePair} array. */ private static final NameValuePair[] EMPTY_NAME_VALUE_PAIR_ARRAY = {}; /** * @deprecated To be removed in 6.0 */ @Deprecated private static final Map CONTENT_TYPE_MAP; static { final ContentType[] contentTypes = { APPLICATION_ATOM_XML, APPLICATION_FORM_URLENCODED, APPLICATION_JSON, APPLICATION_SVG_XML, APPLICATION_XHTML_XML, APPLICATION_XML, IMAGE_BMP, IMAGE_GIF, IMAGE_JPEG, IMAGE_PNG, IMAGE_SVG, IMAGE_TIFF, IMAGE_WEBP, MULTIPART_FORM_DATA, TEXT_HTML, TEXT_PLAIN, TEXT_XML }; final HashMap map = new HashMap<>(); for (final ContentType contentType: contentTypes) { map.put(contentType.getMimeType(), contentType); } CONTENT_TYPE_MAP = Collections.unmodifiableMap(map); } // defaults public static final ContentType DEFAULT_TEXT = TEXT_PLAIN; public static final ContentType DEFAULT_BINARY = APPLICATION_OCTET_STREAM; private final String mimeType; private final Charset charset; private final NameValuePair[] params; ContentType( final String mimeType, final Charset charset) { this.mimeType = mimeType; this.charset = charset; this.params = null; } ContentType( final String mimeType, final Charset charset, final NameValuePair[] params) { this.mimeType = mimeType; this.charset = charset; this.params = params; } public String getMimeType() { return this.mimeType; } public Charset getCharset() { return this.charset; } /** * Gets this Charset if it's non-null, otherwise, return the given {@code defaultCharset}. * * @param defaultCharset A default Charset. * @return this Charset if it's non-null, or the given {@code defaultCharset}. * @since 5.2 */ public Charset getCharset(final Charset defaultCharset) { return this.charset != null ? this.charset : defaultCharset; } /** * @since 4.3 */ public String getParameter(final String name) { Args.notEmpty(name, "Parameter name"); if (this.params == null) { return null; } for (final NameValuePair param: this.params) { if (param.getName().equalsIgnoreCase(name)) { return param.getValue(); } } return null; } /** * Generates textual representation of this content type which can be used as the value * of a {@code Content-Type} header. */ @Override public String toString() { final CharArrayBuffer buf = new CharArrayBuffer(64); buf.append(this.mimeType); if (this.params != null) { buf.append("; "); BasicHeaderValueFormatter.INSTANCE.formatParameters(buf, this.params, false); } else if (this.charset != null) { buf.append("; charset="); buf.append(this.charset.name()); } return buf.toString(); } private static boolean valid(final String s) { for (int i = 0; i < s.length(); i++) { final char ch = s.charAt(i); if (ch == '"' || ch == ',' || ch == ';') { return false; } } return true; } /** * Creates a new instance of {@link ContentType}. * * @param mimeType MIME type. It may not be {@code null} or empty. It may not contain * characters {@code <">, <;>, <,>} reserved by the HTTP specification. * @param charset charset. * @return content type */ public static ContentType create(final String mimeType, final Charset charset) { final String normalizedMimeType = TextUtils.toLowerCase(Args.notBlank(mimeType, "MIME type")); Args.check(valid(normalizedMimeType), "MIME type may not contain reserved characters"); return new ContentType(normalizedMimeType, charset); } /** * Creates a new instance of {@link ContentType} without a charset. * * @param mimeType MIME type. It may not be {@code null} or empty. It may not contain * characters {@code <">, <;>, <,>} reserved by the HTTP specification. * @return content type */ public static ContentType create(final String mimeType) { return create(mimeType, (Charset) null); } /** * Creates a new instance of {@link ContentType}. * * @param mimeType MIME type. It may not be {@code null} or empty. It may not contain * characters {@code <">, <;>, <,>} reserved by the HTTP specification. * @param charset charset. It may not contain characters {@code <">, <;>, <,>} reserved by the HTTP * specification. This parameter is optional. * @return content type * @throws UnsupportedCharsetException Thrown when the named charset is not available in * this instance of the Java virtual machine */ public static ContentType create( final String mimeType, final String charset) throws UnsupportedCharsetException { return create(mimeType, !TextUtils.isBlank(charset) ? Charset.forName(charset) : null); } private static ContentType create(final HeaderElement helem, final boolean strict) { final String mimeType = helem.getName(); if (TextUtils.isBlank(mimeType)) { return null; } return create(helem.getName(), helem.getParameters(), strict); } private static ContentType create(final String mimeType, final NameValuePair[] params, final boolean strict) { Charset charset = null; if (params != null) { for (final NameValuePair param : params) { if (param.getName().equalsIgnoreCase(CHARSET)) { final String s = param.getValue(); if (!TextUtils.isBlank(s)) { try { charset = Charset.forName(s); } catch (final UnsupportedCharsetException ex) { if (strict) { throw ex; } } } break; } } } return new ContentType(mimeType, charset, params != null && params.length > 0 ? params : null); } /** * Creates a new instance of {@link ContentType} with the given parameters. * * @param mimeType MIME type. It may not be {@code null} or empty. It may not contain * characters {@code <">, <;>, <,>} reserved by the HTTP specification. * @param params parameters. * @return content type * * @since 4.4 */ public static ContentType create( final String mimeType, final NameValuePair... params) throws UnsupportedCharsetException { final String type = TextUtils.toLowerCase(Args.notBlank(mimeType, "MIME type")); Args.check(valid(type), "MIME type may not contain reserved characters"); return create(mimeType, params != null ? params.clone() : null, true); } /** * Parses textual representation of {@code Content-Type} value. * * @param s text * @return content type {@code Content-Type} value or null. * @throws UnsupportedCharsetException Thrown when the named charset is not available in * this instance of the Java virtual machine */ public static ContentType parse(final CharSequence s) throws UnsupportedCharsetException { return parse(s, true); } /** * Parses textual representation of {@code Content-Type} value ignoring invalid charsets. * * @param s text * @return content type {@code Content-Type} value or null. * @throws UnsupportedCharsetException Thrown when the named charset is not available in * this instance of the Java virtual machine */ public static ContentType parseLenient(final CharSequence s) throws UnsupportedCharsetException { return parse(s, false); } private static ContentType parse(final CharSequence s, final boolean strict) throws UnsupportedCharsetException { if (TextUtils.isBlank(s)) { return null; } final ParserCursor cursor = new ParserCursor(0, s.length()); final HeaderElement[] elements = BasicHeaderValueParser.INSTANCE.parseElements(s, cursor); if (elements.length > 0) { return create(elements[0], strict); } return null; } /** * Returns {@code Content-Type} for the given MIME type. * * @param mimeType MIME type * @return content type or {@code null} if not known. * * @since 4.5 * * @deprecated Do not use. This method was made public by mistake. */ @Deprecated public static ContentType getByMimeType(final String mimeType) { if (mimeType == null) { return null; } return CONTENT_TYPE_MAP.get(mimeType); } /** * Gets a ContentType's Charset if neither are null, otherwise, return the given {@code defaultCharset}. * * @param contentType the ContentType to test and query. * @param defaultCharset a default Charset. * @return the ContentType's Charset if neither are null, otherwise, return the given {@code defaultCharset}. * @since 5.2 */ public static Charset getCharset(final ContentType contentType, final Charset defaultCharset) { return contentType != null ? contentType.getCharset(defaultCharset) : defaultCharset; } /** * Creates a new instance with this MIME type and the given Charset. * * @param charset charset * @return a new instance with this MIME type and the given Charset. * @since 4.3 */ public ContentType withCharset(final Charset charset) { return create(this.getMimeType(), charset); } /** * Creates a new instance with this MIME type and the given Charset name. * * @param charset name * @return a new instance with this MIME type and the given Charset name. * @throws UnsupportedCharsetException Thrown when the named charset is not available in * this instance of the Java virtual machine * @since 4.3 */ public ContentType withCharset(final String charset) { return create(this.getMimeType(), charset); } /** * Creates a new instance with this MIME type and the given parameters. * * @param params parameters. * @return a new instance with this MIME type and the given parameters. * @since 4.4 */ public ContentType withParameters( final NameValuePair... params) throws UnsupportedCharsetException { if (params.length == 0) { return this; } final Map paramMap = new LinkedHashMap<>(); if (this.params != null) { for (final NameValuePair param: this.params) { paramMap.put(param.getName(), param.getValue()); } } for (final NameValuePair param: params) { paramMap.put(param.getName(), param.getValue()); } final List newParams = new ArrayList<>(paramMap.size() + 1); if (this.charset != null && !paramMap.containsKey(CHARSET)) { newParams.add(new BasicNameValuePair(CHARSET, this.charset.name())); } for (final Map.Entry entry: paramMap.entrySet()) { newParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } return create(this.getMimeType(), newParams.toArray(EMPTY_NAME_VALUE_PAIR_ARRAY), true); } public boolean isSameMimeType(final ContentType contentType) { return contentType != null && mimeType.equalsIgnoreCase(contentType.getMimeType()); } } httpcore5/src/main/java/org/apache/hc/core5/http/HttpVersion.java0100664 0000000 0000000 00000007142 14315123013 023703 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Represents an HTTP version. HTTP uses a "major.minor" numbering * scheme to indicate versions of the protocol. *

* The version of an HTTP message is indicated by an HTTP-Version field * in the first line of the message. *

* * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class HttpVersion extends ProtocolVersion { private static final long serialVersionUID = -5856653513894415344L; /** The protocol name. */ public static final String HTTP = "HTTP"; /** HTTP protocol version 0.9 */ public static final HttpVersion HTTP_0_9 = new HttpVersion(0, 9); /** HTTP protocol version 1.0 */ public static final HttpVersion HTTP_1_0 = new HttpVersion(1, 0); /** HTTP protocol version 1.1 */ public static final HttpVersion HTTP_1_1 = new HttpVersion(1, 1); /** HTTP protocol version 2.0 */ public static final HttpVersion HTTP_2_0 = new HttpVersion(2, 0); public static final HttpVersion HTTP_2 = HTTP_2_0; /** HTTP/1.1 is default */ public static final HttpVersion DEFAULT = HTTP_1_1; /** * All HTTP versions known to HttpCore. */ public static final HttpVersion[] ALL = {HTTP_0_9, HTTP_1_0, HTTP_1_1, HTTP_2_0}; /** * Gets a specific instance or creates a new one. * * @param major the major version * @param minor the minor version * * @return an instance of {@link HttpVersion} with the argument version, never null. * @throws IllegalArgumentException if either major or minor version number is negative * @since 5.0 */ public static HttpVersion get(final int major, final int minor) { for (int i = 0; i < ALL.length; i++) { if (ALL[i].equals(major, minor)) { return ALL[i]; } } // argument checking is done in the constructor return new HttpVersion(major, minor); } /** * Creates an HTTP protocol version designator. * * @param major the major version number of the HTTP protocol * @param minor the minor version number of the HTTP protocol * * @throws IllegalArgumentException if either major or minor version number is negative */ public HttpVersion(final int major, final int minor) { super(HTTP, major, minor); } } httpcore5/src/main/java/org/apache/hc/core5/http/SocketModalCloseable.java0100664 0000000 0000000 00000003304 14245617503 025447 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Timeout; /** * A generic {@link ModalCloseable} backed by a network socket. * * @since 5.0 */ public interface SocketModalCloseable extends ModalCloseable { /** * Returns the socket timeout value. * * @return timeout value. */ Timeout getSocketTimeout(); /** * Sets the socket timeout value. * * @param timeout timeout value */ void setSocketTimeout(Timeout timeout); } httpcore5/src/main/java/org/apache/hc/core5/http/HttpMessage.java0100664 0000000 0000000 00000007506 14403631147 023660 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * HTTP messages consist of requests from client to server and responses * from server to client. * * @since 4.0 */ public interface HttpMessage extends MessageHeaders { /** * Sets protocol version. *

* For incoming messages it represents protocol version this message was transmitted with. * For outgoing messages it represents a hint what protocol version should be used to transmit * the message. * * @since 5.0 */ void setVersion(ProtocolVersion version); /** * Returns protocol version or {@code null} when not available. *

* For incoming messages it represents protocol version this message was transmitted with. * For outgoing messages it represents a hint what protocol version should be used to transmit * the message. * * @since 5.0 */ ProtocolVersion getVersion(); /** * Adds a header to this message. The header will be appended to the end of * the list. * * @param header the header to append. */ void addHeader(Header header); /** * Adds a header to this message. The header will be appended to the end of * the list. * * @param name the name of the header. * @param value the value of the header, taken as the value's {@link Object#toString()}. */ void addHeader(String name, Object value); /** * Overwrites the first header with the same name. The new header will be appended to * the end of the list, if no header with the given name can be found. * * @param header the header to set. */ void setHeader(Header header); /** * Overwrites the first header with the same name. The new header will be appended to * the end of the list, if no header with the given name can be found. * * @param name the name of the header. * @param value the value of the header, taken as the value's {@link Object#toString()}. */ void setHeader(String name, Object value); /** * Overwrites all the headers in the message. * * @param headers the array of headers to set. */ void setHeaders(Header... headers); /** * Removes a header from this message. * * @param header the header to remove. * @return {@code true} if a header was removed as a result of this call. */ boolean removeHeader(Header header); /** * Removes all headers with a certain name from this message. * * @param name The name of the headers to remove. * @return {@code true} if any header was removed as a result of this call. */ boolean removeHeaders(String name); } httpcore5/src/main/java/org/apache/hc/core5/http/MalformedChunkCodingException.java0100664 0000000 0000000 00000004477 14245617503 027346 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; /** * Signals a malformed chunked stream. * * @since 4.0 */ public class MalformedChunkCodingException extends IOException { private static final long serialVersionUID = 2158560246948994524L; /** * Creates a MalformedChunkCodingException without a detail message. */ public MalformedChunkCodingException() { super(); } /** * Creates a MalformedChunkCodingException with the specified detail message. * * @param message The exception detail message */ public MalformedChunkCodingException(final String message) { super(message); } /** * Constructs a new MalformedChunkCodingException with the specified detail message. * * @param format The exception detail message format; see {@link String#format(String, Object...)}. * @param args The exception detail message arguments; see {@link String#format(String, Object...)}. * * @since 5.0 */ public MalformedChunkCodingException(final String format, final Object... args) { super(HttpException.clean(String.format(format, args))); } } httpcore5/src/main/java/org/apache/hc/core5/http/NameValuePair.java0100664 0000000 0000000 00000003137 14245617503 024125 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * A name-value pair parameter used as an element of HTTP messages. * * @since 4.0 */ public interface NameValuePair { /** * Gets the name of this pair. * * @return the name of this pair, never {@code null}. */ String getName(); /** * Gets the value of this pair. * * @return the value of this pair, may be {@code null}. */ String getValue(); } httpcore5/src/main/java/org/apache/hc/core5/http/support/0040775 0000000 0000000 00000000000 14403631147 022300 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractResponseBuilder.java0100664 0000000 0000000 00000006050 14403631147 027732 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.support; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHttpRequest; /** * Builder for {@link BasicHttpRequest} instances. * * @since 5.1 */ public abstract class AbstractResponseBuilder extends AbstractMessageBuilder { private int status; protected AbstractResponseBuilder(final int status) { super(); this.status = status; } public int getStatus() { return status; } public void setStatus(final int status) { this.status = status; } @Override public AbstractResponseBuilder setVersion(final ProtocolVersion version) { super.setVersion(version); return this; } @Override public AbstractResponseBuilder setHeaders(final Header... headers) { super.setHeaders(headers); return this; } @Override public AbstractResponseBuilder addHeader(final Header header) { super.addHeader(header); return this; } @Override public AbstractResponseBuilder addHeader(final String name, final String value) { super.addHeader(name, value); return this; } @Override public AbstractResponseBuilder removeHeader(final Header header) { super.removeHeader(header); return this; } @Override public AbstractResponseBuilder removeHeaders(final String name) { super.removeHeaders(name); return this; } @Override public AbstractResponseBuilder setHeader(final Header header) { super.setHeader(header); return this; } @Override public AbstractResponseBuilder setHeader(final String name, final String value) { super.setHeader(name, value); return this; } @Override protected abstract T build(); } httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractMessageBuilder.java0100664 0000000 0000000 00000011674 14245617503 027534 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.support; import java.util.Iterator; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.HeaderGroup; /** * Abstract {@link HttpMessage} builder. * * @since 5.1 */ public abstract class AbstractMessageBuilder { private ProtocolVersion version; private HeaderGroup headerGroup; protected AbstractMessageBuilder() { } protected void digest(final HttpMessage message) { if (message == null) { return; } setVersion(message.getVersion()); setHeaders(message.headerIterator()); } public ProtocolVersion getVersion() { return version; } public AbstractMessageBuilder setVersion(final ProtocolVersion version) { this.version = version; return this; } public Header[] getHeaders() { return headerGroup != null ? headerGroup.getHeaders() : null; } public Header[] getHeaders(final String name) { return headerGroup != null ? headerGroup.getHeaders(name) : null; } public AbstractMessageBuilder setHeaders(final Header... headers) { if (headerGroup == null) { headerGroup = new HeaderGroup(); } headerGroup.setHeaders(headers); return this; } public AbstractMessageBuilder setHeaders(final Iterator

it) { if (headerGroup == null) { headerGroup = new HeaderGroup(); } else { headerGroup.clear(); } while (it.hasNext()) { headerGroup.addHeader(it.next()); } return this; } public Header[] getFirstHeaders() { return headerGroup != null ? headerGroup.getHeaders() : null; } public Header getFirstHeader(final String name) { return headerGroup != null ? headerGroup.getFirstHeader(name) : null; } public Header getLastHeader(final String name) { return headerGroup != null ? headerGroup.getLastHeader(name) : null; } public AbstractMessageBuilder addHeader(final Header header) { if (headerGroup == null) { headerGroup = new HeaderGroup(); } headerGroup.addHeader(header); return this; } public AbstractMessageBuilder addHeader(final String name, final String value) { if (headerGroup == null) { headerGroup = new HeaderGroup(); } headerGroup.addHeader(new BasicHeader(name, value)); return this; } public AbstractMessageBuilder removeHeader(final Header header) { if (headerGroup == null) { headerGroup = new HeaderGroup(); } headerGroup.removeHeader(header); return this; } public AbstractMessageBuilder removeHeaders(final String name) { if (name == null || headerGroup == null) { return this; } for (final Iterator
i = headerGroup.headerIterator(); i.hasNext(); ) { final Header header = i.next(); if (name.equalsIgnoreCase(header.getName())) { i.remove(); } } return this; } public AbstractMessageBuilder setHeader(final Header header) { if (headerGroup == null) { headerGroup = new HeaderGroup(); } headerGroup.setHeader(header); return this; } public AbstractMessageBuilder setHeader(final String name, final String value) { if (headerGroup == null) { headerGroup = new HeaderGroup(); } headerGroup.setHeader(new BasicHeader(name, value)); return this; } protected abstract T build(); } httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicResponseBuilder.java0100664 0000000 0000000 00000007650 14245617503 027223 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.support; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.util.Args; import java.util.Arrays; /** * Builder for {@link BasicHttpResponse} instances. * * @since 5.1 */ public class BasicResponseBuilder extends AbstractResponseBuilder { protected BasicResponseBuilder(final int status) { super(status); } public static BasicResponseBuilder create(final int status) { Args.checkRange(status, 100, 599, "HTTP status code"); return new BasicResponseBuilder(status); } public static BasicResponseBuilder copy(final HttpResponse response) { Args.notNull(response, "HTTP response"); final BasicResponseBuilder builder = new BasicResponseBuilder(response.getCode()); builder.digest(response); return builder; } @Override public BasicResponseBuilder setVersion(final ProtocolVersion version) { super.setVersion(version); return this; } @Override public BasicResponseBuilder setHeaders(final Header... headers) { super.setHeaders(headers); return this; } @Override public BasicResponseBuilder addHeader(final Header header) { super.addHeader(header); return this; } @Override public BasicResponseBuilder addHeader(final String name, final String value) { super.addHeader(name, value); return this; } @Override public BasicResponseBuilder removeHeader(final Header header) { super.removeHeader(header); return this; } @Override public BasicResponseBuilder removeHeaders(final String name) { super.removeHeaders(name); return this; } @Override public BasicResponseBuilder setHeader(final Header header) { super.setHeader(header); return this; } @Override public BasicResponseBuilder setHeader(final String name, final String value) { super.setHeader(name, value); return this; } @Override public BasicHttpResponse build() { final BasicHttpResponse result = new BasicHttpResponse(getStatus()); result.setVersion(getVersion()); result.setHeaders(getHeaders()); return result; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("BasicResponseBuilder [status="); builder.append(getStatus()); builder.append(", headerGroup="); builder.append(Arrays.toString(getHeaders())); builder.append("]"); return builder.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractRequestBuilder.java0100664 0000000 0000000 00000021226 14315123013 027554 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.support; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.TextUtils; /** * Builder for {@link BasicHttpRequest} instances. * * @since 5.1 */ public abstract class AbstractRequestBuilder extends AbstractMessageBuilder { final private String method; private String scheme; private URIAuthority authority; private String path; private Charset charset; private List parameters; private boolean absoluteRequestUri; protected AbstractRequestBuilder(final String method) { super(); this.method = method; } protected AbstractRequestBuilder(final Method method) { this(method.name()); } protected AbstractRequestBuilder(final String method, final URI uri) { super(); this.method = method; setUri(uri); } protected AbstractRequestBuilder(final Method method, final URI uri) { this(method.name(), uri); } protected AbstractRequestBuilder(final Method method, final String uri) { this(method.name(), uri != null ? URI.create(uri) : null); } protected AbstractRequestBuilder(final String method, final String uri) { this(method, uri != null ? URI.create(uri) : null); } protected void digest(final HttpRequest request) { if (request == null) { return; } setScheme(request.getScheme()); setAuthority(request.getAuthority()); setPath(request.getPath()); this.parameters = null; super.digest(request); } public String getMethod() { return method; } @Override public AbstractRequestBuilder setVersion(final ProtocolVersion version) { super.setVersion(version); return this; } public String getScheme() { return scheme; } public AbstractRequestBuilder setScheme(final String scheme) { this.scheme = scheme; return this; } public URIAuthority getAuthority() { return authority; } public AbstractRequestBuilder setAuthority(final URIAuthority authority) { this.authority = authority; return this; } public AbstractRequestBuilder setHttpHost(final HttpHost httpHost) { if (httpHost == null) { return this; } this.authority = new URIAuthority(httpHost); this.scheme = httpHost.getSchemeName(); return this; } public String getPath() { return path; } public AbstractRequestBuilder setPath(final String path) { this.path = path; return this; } public URI getUri() { final StringBuilder buf = new StringBuilder(); if (this.authority != null) { buf.append(this.scheme != null ? this.scheme : URIScheme.HTTP.id).append("://"); buf.append(this.authority.getHostName()); if (this.authority.getPort() >= 0) { buf.append(":").append(this.authority.getPort()); } } if (this.path == null) { buf.append("/"); } else { if (buf.length() > 0 && !this.path.startsWith("/")) { buf.append("/"); } buf.append(this.path); } return URI.create(buf.toString()); } public AbstractRequestBuilder setUri(final URI uri) { if (uri == null) { this.scheme = null; this.authority = null; this.path = null; } else { this.scheme = uri.getScheme(); if (uri.getHost() != null) { this.authority = new URIAuthority(uri.getRawUserInfo(), uri.getHost(), uri.getPort()); } else if (uri.getRawAuthority() != null) { try { this.authority = URIAuthority.create(uri.getRawAuthority()); } catch (final URISyntaxException ignore) { this.authority = null; } } else { this.authority = null; } final StringBuilder buf = new StringBuilder(); final String rawPath = uri.getRawPath(); if (!TextUtils.isBlank(rawPath)) { buf.append(rawPath); } else { buf.append("/"); } final String query = uri.getRawQuery(); if (query != null) { buf.append('?').append(query); } this.path = buf.toString(); } return this; } public AbstractRequestBuilder setUri(final String uri) { setUri(uri != null ? URI.create(uri) : null); return this; } @Override public AbstractRequestBuilder setHeaders(final Header... headers) { super.setHeaders(headers); return this; } @Override public AbstractRequestBuilder addHeader(final Header header) { super.addHeader(header); return this; } @Override public AbstractRequestBuilder addHeader(final String name, final String value) { super.addHeader(name, value); return this; } @Override public AbstractRequestBuilder removeHeader(final Header header) { super.removeHeader(header); return this; } @Override public AbstractRequestBuilder removeHeaders(final String name) { super.removeHeaders(name); return this; } @Override public AbstractRequestBuilder setHeader(final Header header) { super.setHeader(header); return this; } @Override public AbstractRequestBuilder setHeader(final String name, final String value) { super.setHeader(name, value); return this; } public Charset getCharset() { return charset; } public AbstractRequestBuilder setCharset(final Charset charset) { this.charset = charset; return this; } public List getParameters() { return parameters != null ? new ArrayList<>(parameters) : null; } public AbstractRequestBuilder addParameter(final NameValuePair nvp) { if (nvp == null) { return this; } if (parameters == null) { parameters = new LinkedList<>(); } parameters.add(nvp); return this; } public AbstractRequestBuilder addParameter(final String name, final String value) { return addParameter(new BasicNameValuePair(name, value)); } public AbstractRequestBuilder addParameters(final NameValuePair... nvps) { for (final NameValuePair nvp : nvps) { addParameter(nvp); } return this; } public boolean isAbsoluteRequestUri() { return absoluteRequestUri; } public AbstractRequestBuilder setAbsoluteRequestUri(final boolean absoluteRequestUri) { this.absoluteRequestUri = absoluteRequestUri; return this; } } httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicRequestBuilder.java0100664 0000000 0000000 00000024016 14245617503 027050 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.support; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.util.Args; /** * Builder for {@link BasicHttpRequest} instances. * * @since 5.1 */ public class BasicRequestBuilder extends AbstractRequestBuilder { BasicRequestBuilder(final String method) { super(method); } BasicRequestBuilder(final Method method) { super(method); } BasicRequestBuilder(final String method, final URI uri) { super(method, uri); } BasicRequestBuilder(final Method method, final URI uri) { super(method, uri); } BasicRequestBuilder(final Method method, final String uri) { super(method, uri); } BasicRequestBuilder(final String method, final String uri) { super(method, uri); } public static BasicRequestBuilder create(final String method) { Args.notBlank(method, "HTTP method"); return new BasicRequestBuilder(method); } public static BasicRequestBuilder get() { return new BasicRequestBuilder(Method.GET); } public static BasicRequestBuilder get(final URI uri) { return new BasicRequestBuilder(Method.GET, uri); } public static BasicRequestBuilder get(final String uri) { return new BasicRequestBuilder(Method.GET, uri); } public static BasicRequestBuilder head() { return new BasicRequestBuilder(Method.HEAD); } public static BasicRequestBuilder head(final URI uri) { return new BasicRequestBuilder(Method.HEAD, uri); } public static BasicRequestBuilder head(final String uri) { return new BasicRequestBuilder(Method.HEAD, uri); } public static BasicRequestBuilder patch() { return new BasicRequestBuilder(Method.PATCH); } public static BasicRequestBuilder patch(final URI uri) { return new BasicRequestBuilder(Method.PATCH, uri); } public static BasicRequestBuilder patch(final String uri) { return new BasicRequestBuilder(Method.PATCH, uri); } public static BasicRequestBuilder post() { return new BasicRequestBuilder(Method.POST); } public static BasicRequestBuilder post(final URI uri) { return new BasicRequestBuilder(Method.POST, uri); } public static BasicRequestBuilder post(final String uri) { return new BasicRequestBuilder(Method.POST, uri); } public static BasicRequestBuilder put() { return new BasicRequestBuilder(Method.PUT); } public static BasicRequestBuilder put(final URI uri) { return new BasicRequestBuilder(Method.PUT, uri); } public static BasicRequestBuilder put(final String uri) { return new BasicRequestBuilder(Method.PUT, uri); } public static BasicRequestBuilder delete() { return new BasicRequestBuilder(Method.DELETE); } public static BasicRequestBuilder delete(final URI uri) { return new BasicRequestBuilder(Method.DELETE, uri); } public static BasicRequestBuilder delete(final String uri) { return new BasicRequestBuilder(Method.DELETE, uri); } public static BasicRequestBuilder trace() { return new BasicRequestBuilder(Method.TRACE); } public static BasicRequestBuilder trace(final URI uri) { return new BasicRequestBuilder(Method.TRACE, uri); } public static BasicRequestBuilder trace(final String uri) { return new BasicRequestBuilder(Method.TRACE, uri); } public static BasicRequestBuilder options() { return new BasicRequestBuilder(Method.OPTIONS); } public static BasicRequestBuilder options(final URI uri) { return new BasicRequestBuilder(Method.OPTIONS, uri); } public static BasicRequestBuilder options(final String uri) { return new BasicRequestBuilder(Method.OPTIONS, uri); } public static BasicRequestBuilder copy(final HttpRequest request) { Args.notNull(request, "HTTP request"); final BasicRequestBuilder builder = new BasicRequestBuilder(request.getMethod()); builder.digest(request); return builder; } @Override public BasicRequestBuilder setVersion(final ProtocolVersion version) { super.setVersion(version); return this; } @Override public BasicRequestBuilder setUri(final URI uri) { super.setUri(uri); return this; } @Override public BasicRequestBuilder setUri(final String uri) { super.setUri(uri); return this; } @Override public BasicRequestBuilder setScheme(final String scheme) { super.setScheme(scheme); return this; } @Override public BasicRequestBuilder setAuthority(final URIAuthority authority) { super.setAuthority(authority); return this; } /** * @since 5.1 */ @Override public BasicRequestBuilder setHttpHost(final HttpHost httpHost) { super.setHttpHost(httpHost); return this; } @Override public BasicRequestBuilder setPath(final String path) { super.setPath(path); return this; } @Override public BasicRequestBuilder setHeaders(final Header... headers) { super.setHeaders(headers); return this; } @Override public BasicRequestBuilder addHeader(final Header header) { super.addHeader(header); return this; } @Override public BasicRequestBuilder addHeader(final String name, final String value) { super.addHeader(name, value); return this; } @Override public BasicRequestBuilder removeHeader(final Header header) { super.removeHeader(header); return this; } @Override public BasicRequestBuilder removeHeaders(final String name) { super.removeHeaders(name); return this; } @Override public BasicRequestBuilder setHeader(final Header header) { super.setHeader(header); return this; } @Override public BasicRequestBuilder setHeader(final String name, final String value) { super.setHeader(name, value); return this; } @Override public BasicRequestBuilder setCharset(final Charset charset) { super.setCharset(charset); return this; } @Override public BasicRequestBuilder addParameter(final NameValuePair nvp) { super.addParameter(nvp); return this; } @Override public BasicRequestBuilder addParameter(final String name, final String value) { super.addParameter(name, value); return this; } @Override public BasicRequestBuilder addParameters(final NameValuePair... nvps) { super.addParameters(nvps); return this; } @Override public BasicRequestBuilder setAbsoluteRequestUri(final boolean absoluteRequestUri) { super.setAbsoluteRequestUri(absoluteRequestUri); return this; } @Override public BasicHttpRequest build() { String path = getPath(); final List parameters = getParameters(); if (parameters != null && !parameters.isEmpty()) { try { final URI uri = new URIBuilder(path) .setCharset(getCharset()) .addParameters(parameters) .build(); path = uri.toASCIIString(); } catch (final URISyntaxException ex) { // should never happen } } final BasicHttpRequest result = new BasicHttpRequest(getMethod(), getScheme(), getAuthority(), path); result.setVersion(getVersion()); result.setHeaders(getHeaders()); result.setAbsoluteRequestUri(isAbsoluteRequestUri()); return result; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("BasicRequestBuilder [method="); builder.append(getMethod()); builder.append(", scheme="); builder.append(getScheme()); builder.append(", authority="); builder.append(getAuthority()); builder.append(", path="); builder.append(getPath()); builder.append(", parameters="); builder.append(getParameters()); builder.append(", headerGroup="); builder.append(Arrays.toString(getHeaders())); builder.append("]"); return builder.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/ExceptionListener.java0100664 0000000 0000000 00000004153 14245617503 025077 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface ExceptionListener { ExceptionListener NO_OP = new ExceptionListener() { @Override public void onError(final Exception ex) { // no-op } @Override public void onError(final HttpConnection connection, final Exception ex) { // no-op } }; ExceptionListener STD_ERR = new ExceptionListener() { @Override public void onError(final Exception ex) { ex.printStackTrace(); } @Override public void onError(final HttpConnection connection, final Exception ex) { ex.printStackTrace(); } }; void onError(Exception ex); void onError(HttpConnection connection, Exception ex); } httpcore5/src/main/java/org/apache/hc/core5/http/Chars.java0100664 0000000 0000000 00000003251 14245617503 022471 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Commons chars used by HTTP/1.1 protocol. * * @since 5.0 */ public final class Chars { public static final int CR = 13; // public static final int LF = 10; // public static final int SP = 32; // public static final int HT = 9; // public static final int DEL = 127; // private Chars() { } } httpcore5/src/main/java/org/apache/hc/core5/http/HttpResponseFactory.java0100664 0000000 0000000 00000003471 14245617503 025423 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * A factory for {@link HttpResponse} objects. * * @since 4.0 */ public interface HttpResponseFactory { /** * Creates response message with the given code and reason phrase. * * @param status the status code * @param reasonPhrase the reason phrase * * @return response message * * @since 5.0 */ T newHttpResponse(int status, String reasonPhrase); /** * Creates a new response with the given code. * * @param status the status code * * @return response message */ T newHttpResponse(int status); } httpcore5/src/main/java/org/apache/hc/core5/http/message/0040775 0000000 0000000 00000000000 14435411677 022221 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/message/package-info.java0100664 0000000 0000000 00000002501 14245617503 025376 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core HTTP message components, message element parser * and writer APIs and their default implementations. */ package org.apache.hc.core5.http.message; httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicLineParser.java0100664 0000000 0000000 00000021656 14245617503 026074 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.BitSet; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Tokenizer; /** * Default {@link org.apache.hc.core5.http.message.LineParser} implementation. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class BasicLineParser implements LineParser { public final static BasicLineParser INSTANCE = new BasicLineParser(); // IMPORTANT! // These private static variables must be treated as immutable and never exposed outside this class private static final BitSet FULL_STOP = Tokenizer.INIT_BITSET('.'); private static final BitSet BLANKS = Tokenizer.INIT_BITSET(' ', '\t'); private static final BitSet COLON = Tokenizer.INIT_BITSET(':'); /** * A version of the protocol to parse. * The version is typically not relevant, but the protocol name. */ private final ProtocolVersion protocol; private final Tokenizer tokenizer; /** * Creates a new line parser for the given HTTP-like protocol. * * @param proto a version of the protocol to parse, or * {@code null} for HTTP. The actual version * is not relevant, only the protocol name. */ public BasicLineParser(final ProtocolVersion proto) { this.protocol = proto != null? proto : HttpVersion.HTTP_1_1; this.tokenizer = Tokenizer.INSTANCE; } /** * Creates a new line parser for HTTP. */ public BasicLineParser() { this(null); } ProtocolVersion parseProtocolVersion( final CharArrayBuffer buffer, final ParserCursor cursor) throws ParseException { final String protoname = this.protocol.getProtocol(); final int protolength = protoname.length(); this.tokenizer.skipWhiteSpace(buffer, cursor); final int pos = cursor.getPos(); // long enough for "HTTP/1.1"? if (pos + protolength + 4 > cursor.getUpperBound()) { throw new ParseException("Invalid protocol version", buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); } // check the protocol name and slash boolean ok = true; for (int i = 0; ok && (i < protolength); i++) { ok = buffer.charAt(pos + i) == protoname.charAt(i); } if (ok) { ok = buffer.charAt(pos + protolength) == '/'; } if (!ok) { throw new ParseException("Invalid protocol version", buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); } cursor.updatePos(pos + protolength + 1); final String token1 = this.tokenizer.parseToken(buffer, cursor, FULL_STOP); final int major; try { major = Integer.parseInt(token1); } catch (final NumberFormatException e) { throw new ParseException("Invalid protocol major version number", buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); } if (cursor.atEnd()) { throw new ParseException("Invalid protocol version", buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); } cursor.updatePos(cursor.getPos() + 1); final String token2 = this.tokenizer.parseToken(buffer, cursor, BLANKS); final int minor; try { minor = Integer.parseInt(token2); } catch (final NumberFormatException e) { throw new ParseException("Invalid protocol minor version number", buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); } return HttpVersion.get(major, minor); } /** * Parses a request line. * * @param buffer a buffer holding the line to parse * * @return the parsed request line * * @throws ParseException in case of a parse error */ @Override public RequestLine parseRequestLine(final CharArrayBuffer buffer) throws ParseException { Args.notNull(buffer, "Char array buffer"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); this.tokenizer.skipWhiteSpace(buffer, cursor); final String method = this.tokenizer.parseToken(buffer, cursor, BLANKS); if (TextUtils.isEmpty(method)) { throw new ParseException("Invalid request line", buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); } this.tokenizer.skipWhiteSpace(buffer, cursor); final String uri = this.tokenizer.parseToken(buffer, cursor, BLANKS); if (TextUtils.isEmpty(uri)) { throw new ParseException("Invalid request line", buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); } final ProtocolVersion ver = parseProtocolVersion(buffer, cursor); this.tokenizer.skipWhiteSpace(buffer, cursor); if (!cursor.atEnd()) { throw new ParseException("Invalid request line", buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); } return new RequestLine(method, uri, ver); } @Override public StatusLine parseStatusLine(final CharArrayBuffer buffer) throws ParseException { Args.notNull(buffer, "Char array buffer"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); this.tokenizer.skipWhiteSpace(buffer, cursor); final ProtocolVersion ver = parseProtocolVersion(buffer, cursor); this.tokenizer.skipWhiteSpace(buffer, cursor); final String s = this.tokenizer.parseToken(buffer, cursor, BLANKS); for (int i = 0; i < s.length(); i++) { if (!Character.isDigit(s.charAt(i))) { throw new ParseException("Status line contains invalid status code", buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); } } final int statusCode; try { statusCode = Integer.parseInt(s); } catch (final NumberFormatException e) { throw new ParseException("Status line contains invalid status code", buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); } final String text = buffer.substringTrimmed(cursor.getPos(), cursor.getUpperBound()); return new StatusLine(ver, statusCode, text); } @Override public Header parseHeader(final CharArrayBuffer buffer) throws ParseException { Args.notNull(buffer, "Char array buffer"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); this.tokenizer.skipWhiteSpace(buffer, cursor); final String name = this.tokenizer.parseToken(buffer, cursor, COLON); if (cursor.getPos() == cursor.getLowerBound() || cursor.getPos() == cursor.getUpperBound() || buffer.charAt(cursor.getPos()) != ':' || TextUtils.isEmpty(name) || Tokenizer.isWhitespace(buffer.charAt(cursor.getPos() - 1))) { throw new ParseException("Invalid header", buffer, cursor.getLowerBound(), cursor.getUpperBound(), cursor.getPos()); } final String value = buffer.substringTrimmed(cursor.getPos() + 1, cursor.getUpperBound()); return new BasicHeader(name, value); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicNameValuePair.java0100664 0000000 0000000 00000006604 14403631147 026511 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.io.Serializable; import java.util.Objects; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.LangUtils; import org.apache.hc.core5.util.TextUtils; /** * Basic implementation of {@link NameValuePair}. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class BasicNameValuePair implements NameValuePair, Serializable { private static final long serialVersionUID = -6437800749411518984L; private final String name; private final String value; /** * Default Constructor taking a name and a value. The value may be null. * * @param name The name. * @param value The value. */ public BasicNameValuePair(final String name, final String value) { super(); this.name = Args.notNull(name, "Name"); this.value = value; } @Override public String getName() { return this.name; } @Override public String getValue() { return this.value; } @Override public String toString() { // don't call complex default formatting for a simple toString if (this.value == null) { return name; } final int len = this.name.length() + 1 + this.value.length(); final StringBuilder buffer = new StringBuilder(len); buffer.append(this.name); buffer.append("="); buffer.append(this.value); return buffer.toString(); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof BasicNameValuePair) { final BasicNameValuePair that = (BasicNameValuePair) obj; return this.name.equalsIgnoreCase(that.name) && Objects.equals(this.value, that.value); } return false; } @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, TextUtils.toLowerCase(this.name)); hash = LangUtils.hashCode(hash, this.value); return hash; } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHeaderElement.java0100664 0000000 0000000 00000007666 14403631147 026533 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.Args; /** * Basic implementation of {@link HeaderElement} * * @since 4.0 */ public class BasicHeaderElement implements HeaderElement { private static final NameValuePair[] EMPTY_NAME_VALUE_PAIR_ARRAY = {}; private final String name; private final String value; private final NameValuePair[] parameters; /** * Constructor with name, value and parameters. * * @param name header element name * @param value header element value. May be {@code null} * @param parameters header element parameters. May be {@code null}. * Parameters are copied by reference, not by value */ public BasicHeaderElement( final String name, final String value, final NameValuePair[] parameters) { super(); this.name = Args.notNull(name, "Name"); this.value = value; if (parameters != null) { this.parameters = parameters; } else { this.parameters = EMPTY_NAME_VALUE_PAIR_ARRAY; } } /** * Constructor with name and value. * * @param name header element name * @param value header element value. May be {@code null} */ public BasicHeaderElement(final String name, final String value) { this(name, value, null); } @Override public String getName() { return this.name; } @Override public String getValue() { return this.value; } @Override public NameValuePair[] getParameters() { return this.parameters.clone(); } @Override public int getParameterCount() { return this.parameters.length; } @Override public NameValuePair getParameter(final int index) { // ArrayIndexOutOfBoundsException is appropriate return this.parameters[index]; } @Override public NameValuePair getParameterByName(final String name) { Args.notNull(name, "Name"); NameValuePair found = null; for (final NameValuePair current : this.parameters) { if (current.getName().equalsIgnoreCase(name)) { found = current; break; } } return found; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append(this.name); if (this.value != null) { buffer.append("="); buffer.append(this.value); } for (final NameValuePair parameter : this.parameters) { buffer.append("; "); buffer.append(parameter); } return buffer.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/LazyLaxLineParser.java0100664 0000000 0000000 00000004426 14245617503 026433 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Extension of {@link org.apache.hc.core5.http.message.BasicLineParser} that defers parsing of * header values. Header value is parsed only if accessed with * {@link org.apache.hc.core5.http.Header#getValue()}. *

* This parser unlike {@link BasicLineParser} and {@link LazyLineParser} * will not reject headers containing whitespaces between the header field * name and colon. *

* This parser should be used to parse response messages on the client server * or to parse both request and response messages by an intermediary (proxy). * * @since 5.0 */ public class LazyLaxLineParser extends BasicLineParser { public final static LazyLaxLineParser INSTANCE = new LazyLaxLineParser(); @Override public Header parseHeader(final CharArrayBuffer buffer) throws ParseException { Args.notNull(buffer, "Char array buffer"); return new BufferedHeader(buffer, false); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/StatusLine.java0100664 0000000 0000000 00000015622 14245617503 025155 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.io.Serializable; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Args; /** * HTTP/1.1 status line. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class StatusLine implements Serializable { private static final long serialVersionUID = -2443303766890459269L; /** * The protocol version. */ private final ProtocolVersion protoVersion; /** * The status code. */ private final int statusCode; /** * The status code class. */ private final StatusClass statusClass; /** * The reason phrase. */ private final String reasonPhrase; public StatusLine(final HttpResponse response) { super(); Args.notNull(response, "Response"); this.protoVersion = response.getVersion() != null ? response.getVersion() : HttpVersion.HTTP_1_1; this.statusCode = response.getCode(); this.statusClass = StatusClass.from(this.statusCode); this.reasonPhrase = response.getReasonPhrase(); } /** * Creates a new status line with the given version, status, and reason. * * @param version the protocol version of the response * @param statusCode the status code of the response * @param reasonPhrase the reason phrase to the status code, or * {@code null} */ public StatusLine(final ProtocolVersion version, final int statusCode, final String reasonPhrase) { super(); this.statusCode = Args.notNegative(statusCode, "Status code"); this.statusClass = StatusClass.from(this.statusCode); this.protoVersion = version != null ? version : HttpVersion.HTTP_1_1; this.reasonPhrase = reasonPhrase; } public int getStatusCode() { return this.statusCode; } public StatusClass getStatusClass() { return this.statusClass; } /** * Whether this status code is in the HTTP series {@link StatusClass#INFORMATIONAL}. * * @since 5.1 */ public boolean isInformational() { return getStatusClass() == StatusClass.INFORMATIONAL; } /** * Whether this status code is in the HTTP series {@link StatusClass#SUCCESSFUL}. * * @since 5.1 */ public boolean isSuccessful() { return getStatusClass() == StatusClass.SUCCESSFUL; } /** * Whether this status code is in the HTTP series {@link StatusClass#REDIRECTION}. * * @since 5.1 */ public boolean isRedirection() { return getStatusClass() == StatusClass.REDIRECTION; } /** * Whether this status code is in the HTTP series {@link StatusClass#CLIENT_ERROR}. * * @since 5.1 */ public boolean isClientError() { return getStatusClass() == StatusClass.CLIENT_ERROR; } /** * Whether this status code is in the HTTP series {@link StatusClass#SERVER_ERROR}. * * @since 5.1 */ public boolean isServerError() { return getStatusClass() == StatusClass.SERVER_ERROR; } /** * Whether this status code is in the HTTP series {@link StatusClass#CLIENT_ERROR} * or {@link StatusClass#SERVER_ERROR}. * * @since 5.1 */ public boolean isError() { return isClientError() || isServerError(); } public ProtocolVersion getProtocolVersion() { return this.protoVersion; } public String getReasonPhrase() { return this.reasonPhrase; } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append(this.protoVersion).append(" ").append(this.statusCode).append(" "); if (this.reasonPhrase != null) { buf.append(this.reasonPhrase); } return buf.toString(); } /** * Standard classes of HTTP status codes, plus {@code OTHER} for non-standard codes. */ public enum StatusClass { /** * Informational {@code 1xx} HTTP status codes. */ INFORMATIONAL, /** * Successful {@code 2xx} HTTP status codes. */ SUCCESSFUL, /** * Redirection {@code 3xx} HTTP status codes. */ REDIRECTION, /** * Client Error {@code 4xx} HTTP status codes. */ CLIENT_ERROR, /** * {@code 5xx} HTTP status codes. */ SERVER_ERROR, /** * Any other HTTP status codes (e.g. non-standard status codes). */ OTHER; /** * Gets the response status class for the given status code. * * @param statusCode response status code to get the class for. * @return class of the response status code. */ public static StatusClass from(final int statusCode) { final StatusClass statusClass; switch (statusCode / 100) { case 1: statusClass = INFORMATIONAL; break; case 2: statusClass = SUCCESSFUL; break; case 3: statusClass = REDIRECTION; break; case 4: statusClass = CLIENT_ERROR; break; case 5: statusClass = SERVER_ERROR; break; default: statusClass = OTHER; break; } return statusClass; } } } httpcore5/src/main/java/org/apache/hc/core5/http/message/HeaderValueFormatter.java0100664 0000000 0000000 00000006531 14245617503 027132 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.CharArrayBuffer; /** * Interface for formatting elements of a header value. * Instances of this interface are expected to be stateless and thread-safe. * * @since 4.0 */ public interface HeaderValueFormatter { /** * Formats an array of header elements. * * @param buffer buffer to write formatted content to. * @param elems the header elements to format * @param quote {@code true} to always format with quoted values, * {@code false} to use quotes only when necessary */ void formatElements(CharArrayBuffer buffer, HeaderElement[] elems, boolean quote); /** * Formats one header element. * * @param buffer buffer to write formatted content to. * @param elem the header element to format * @param quote {@code true} to always format with quoted values, * {@code false} to use quotes only when necessary */ void formatHeaderElement(CharArrayBuffer buffer, HeaderElement elem, boolean quote); /** * Formats the parameters of a header element. * That's a list of name-value pairs, to be separated by semicolons. * This method will not generate a leading semicolon. * * @param buffer buffer to write formatted content to. * @param nvps the parameters (name-value pairs) to format * @param quote {@code true} to always format with quoted values, * {@code false} to use quotes only when necessary */ void formatParameters(CharArrayBuffer buffer, NameValuePair[] nvps, boolean quote); /** * Formats one name-value pair, where the value is optional. * * @param buffer buffer to write formatted content to. * @param nvp the name-value pair to format * @param quote {@code true} to always format with a quoted value, * {@code false} to use quotes only when necessary */ void formatNameValuePair(CharArrayBuffer buffer, NameValuePair nvp, boolean quote); } httpcore5/src/main/java/org/apache/hc/core5/http/message/HeaderGroup.java0100664 0000000 0000000 00000030220 14403631147 025252 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.MessageHeaders; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.util.CharArrayBuffer; import org.apache.hc.core5.util.TextUtils; /** * A class for combining a set of headers. This class allows for multiple headers with the same name * and keeps track of the order in which headers were added. * * @since 4.0 */ public class HeaderGroup implements MessageHeaders, Serializable { private static final long serialVersionUID = 2608834160639271617L; private static final Header[] EMPTY = new Header[] {}; /** The list of headers for this group, in the order in which they were added */ private final List

headers; /** * Constructor for HeaderGroup. */ public HeaderGroup() { this.headers = new ArrayList<>(16); } /** * Removes all headers. */ public void clear() { headers.clear(); } /** * Adds the given header to the group. The order in which this header was * added is preserved. * * @param header the header to add */ public void addHeader(final Header header) { if (header == null) { return; } headers.add(header); } /** * Removes the first given header. * * @param header the header to remove * @return {@code true} if a header was removed as a result of this call. */ public boolean removeHeader(final Header header) { if (header == null) { return false; } for (int i = 0; i < this.headers.size(); i++) { final Header current = this.headers.get(i); if (headerEquals(header, current)) { this.headers.remove(current); return true; } } return false; } private boolean headerEquals(final Header header1, final Header header2) { return header2 == header1 || header2.getName().equalsIgnoreCase(header1.getName()) && Objects.equals(header1.getValue(), header2.getValue()); } /** * Removes all headers that match the given header. * * @param header the header to remove * @return {@code true} if any header was removed as a result of this call. * * @since 5.0 */ public boolean removeHeaders(final Header header) { if (header == null) { return false; } boolean removed = false; for (final Iterator
iterator = headerIterator(); iterator.hasNext();) { final Header current = iterator.next(); if (headerEquals(header, current)) { iterator.remove(); removed = true; } } return removed; } /** * Replaces the first occurrence of the header with the same name. If no header with * the same name is found the given header is added to the end of the list. * * @param header the new header that should replace the first header with the same * name if present in the list. * * @since 5.0 */ public void setHeader(final Header header) { if (header == null) { return; } for (int i = 0; i < this.headers.size(); i++) { final Header current = this.headers.get(i); if (current.getName().equalsIgnoreCase(header.getName())) { this.headers.set(i, header); return; } } this.headers.add(header); } /** * Sets all of the headers contained within this group overriding any * existing headers. The headers are added in the order in which they appear * in the array. * * @param headers the headers to set */ public void setHeaders(final Header... headers) { clear(); if (headers == null) { return; } Collections.addAll(this.headers, headers); } /** * Gets a header representing all of the header values with the given name. * If more that one header with the given name exists the values will be * combined with a ",". * *

Header name comparison is case insensitive. * * @param name the name of the header(s) to get * @return a header with a condensed value or {@code null} if no * headers by the given name are present */ public Header getCondensedHeader(final String name) { final Header[] hdrs = getHeaders(name); if (hdrs.length == 0) { return null; } else if (hdrs.length == 1) { return hdrs[0]; } else { final CharArrayBuffer valueBuffer = new CharArrayBuffer(128); valueBuffer.append(hdrs[0].getValue()); for (int i = 1; i < hdrs.length; i++) { valueBuffer.append(", "); valueBuffer.append(hdrs[i].getValue()); } return new BasicHeader(TextUtils.toLowerCase(name), valueBuffer.toString()); } } /** * Gets all of the headers with the given name. The returned array * maintains the relative order in which the headers were added. * *

Header name comparison is case insensitive. * * @param name the name of the header(s) to get * * @return an array of length ≥ 0 */ @Override public Header[] getHeaders(final String name) { List

headersFound = null; for (int i = 0; i < this.headers.size(); i++) { final Header header = this.headers.get(i); if (header.getName().equalsIgnoreCase(name)) { if (headersFound == null) { headersFound = new ArrayList<>(); } headersFound.add(header); } } return headersFound != null ? headersFound.toArray(EMPTY) : EMPTY; } /** * Gets the first header with the given name. * *

Header name comparison is case insensitive. * * @param name the name of the header to get * @return the first header or {@code null} */ @Override public Header getFirstHeader(final String name) { for (int i = 0; i < this.headers.size(); i++) { final Header header = this.headers.get(i); if (header.getName().equalsIgnoreCase(name)) { return header; } } return null; } /** * Gets single first header with the given name. * *

Header name comparison is case insensitive. * * @param name the name of the header to get * @return the first header or {@code null} * @throws ProtocolException in case multiple headers with the given name are found. */ @Override public Header getHeader(final String name) throws ProtocolException { int count = 0; Header singleHeader = null; for (int i = 0; i < this.headers.size(); i++) { final Header header = this.headers.get(i); if (header.getName().equalsIgnoreCase(name)) { singleHeader = header; count++; } } if (count > 1) { throw new ProtocolException("multiple '%s' headers found", name); } return singleHeader; } /** * Gets the last header with the given name. * *

Header name comparison is case insensitive. * * @param name the name of the header to get * @return the last header or {@code null} */ @Override public Header getLastHeader(final String name) { // start at the end of the list and work backwards for (int i = headers.size() - 1; i >= 0; i--) { final Header header = headers.get(i); if (header.getName().equalsIgnoreCase(name)) { return header; } } return null; } /** * Gets all of the headers contained within this group. * * @return an array of length ≥ 0 */ @Override public Header[] getHeaders() { return headers.toArray(EMPTY); } /** * Tests if headers with the given name are contained within this group. * *

Header name comparison is case insensitive. * * @param name the header name to test for * @return {@code true} if at least one header with the name is * contained, {@code false} otherwise */ @Override public boolean containsHeader(final String name) { for (int i = 0; i < this.headers.size(); i++) { final Header header = this.headers.get(i); if (header.getName().equalsIgnoreCase(name)) { return true; } } return false; } /** * Checks if a certain header is present in this message and how many times. *

Header name comparison is case insensitive. * * @param name the header name to check for. * @return number of occurrences of the header in the message. */ @Override public int countHeaders(final String name) { int count = 0; for (int i = 0; i < this.headers.size(); i++) { final Header header = this.headers.get(i); if (header.getName().equalsIgnoreCase(name)) { count++; } } return count; } /** * Returns an iterator over this group of headers. * * @return iterator over this group of headers. * * @since 5.0 */ @Override public Iterator

headerIterator() { return new BasicListHeaderIterator(this.headers, null); } /** * Returns an iterator over the headers with a given name in this group. * * @param name the name of the headers over which to iterate, or * {@code null} for all headers * * @return iterator over some headers in this group. * * @since 5.0 */ @Override public Iterator
headerIterator(final String name) { return new BasicListHeaderIterator(this.headers, name); } /** * Removes all headers with a given name in this group. * * @param name the name of the headers to be removed. * @return {@code true} if any header was removed as a result of this call. * * @since 5.0 */ public boolean removeHeaders(final String name) { if (name == null) { return false; } boolean removed = false; for (final Iterator
iterator = headerIterator(); iterator.hasNext(); ) { final Header header = iterator.next(); if (header.getName().equalsIgnoreCase(name)) { iterator.remove(); removed = true; } } return removed; } @Override public String toString() { return this.headers.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/RequestLine.java0100664 0000000 0000000 00000005663 14245617503 025326 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.io.Serializable; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Args; /** * HTTP/1.1 request line. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class RequestLine implements Serializable { private static final long serialVersionUID = 2810581718468737193L; private final ProtocolVersion protoversion; private final String method; private final String uri; public RequestLine(final HttpRequest request) { super(); Args.notNull(request, "Request"); this.method = request.getMethod(); this.uri = request.getRequestUri(); this.protoversion = request.getVersion() != null ? request.getVersion() : HttpVersion.HTTP_1_1; } public RequestLine(final String method, final String uri, final ProtocolVersion version) { super(); this.method = Args.notNull(method, "Method"); this.uri = Args.notNull(uri, "URI"); this.protoversion = version != null ? version : HttpVersion.HTTP_1_1; } public String getMethod() { return this.method; } public ProtocolVersion getProtocolVersion() { return this.protoversion; } public String getUri() { return this.uri; } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append(this.method).append(" ").append(this.uri).append(" ").append(this.protoversion); return buf.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicClassicHttpRequest.java0100664 0000000 0000000 00000010146 14245617503 027612 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.net.URIAuthority; import java.net.URI; /** * Basic implementation of {@link ClassicHttpRequest}. * * @since 5.0 */ public class BasicClassicHttpRequest extends BasicHttpRequest implements ClassicHttpRequest { private static final long serialVersionUID = 1L; private HttpEntity entity; /** * Creates request message with the given method, host and request path. * * @param method request method. * @param scheme request scheme. * @param authority request authority. * @param path request path. * * @since 5.1 */ public BasicClassicHttpRequest(final String method, final String scheme, final URIAuthority authority, final String path) { super(method, scheme, authority, path); } /** * Creates request message with the given method and request path. * * @param method request method. * @param path request path. */ public BasicClassicHttpRequest(final String method, final String path) { super(method, path); } /** * Creates request message with the given method, host and request path. * * @param method request method. * @param host request host. * @param path request path. */ public BasicClassicHttpRequest(final String method, final HttpHost host, final String path) { super(method, host, path); } /** * Creates request message with the given method, request URI. * * @param method request method. * @param requestUri request URI. */ public BasicClassicHttpRequest(final String method, final URI requestUri) { super(method, requestUri); } /** * Creates request message with the given method and request path. * * @param method request method. * @param path request path. */ public BasicClassicHttpRequest(final Method method, final String path) { super(method, path); } /** * Creates request message with the given method, host and request path. * * @param method request method. * @param host request host. * @param path request path. */ public BasicClassicHttpRequest(final Method method, final HttpHost host, final String path) { super(method, host, path); } /** * Creates request message with the given method, request URI. * * @param method request method. * @param requestUri request URI. */ public BasicClassicHttpRequest(final Method method, final URI requestUri) { super(method, requestUri); } @Override public HttpEntity getEntity() { return this.entity; } @Override public void setEntity(final HttpEntity entity) { this.entity = entity; } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHeader.java0100664 0000000 0000000 00000006227 14403631147 025211 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.io.Serializable; import java.util.Objects; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.util.Args; /** * Immutable {@link Header}. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class BasicHeader implements Header, Cloneable, Serializable { private static final long serialVersionUID = -5427236326487562174L; private final String name; private final boolean sensitive; private final String value; /** * Default constructor * * @param name the header name * @param value the header value, taken as the value's {@link #toString()}. */ public BasicHeader(final String name, final Object value) { this(name, value, false); } /** * Constructor with sensitivity flag * * @param name the header name * @param value the header value, taken as the value's {@link #toString()}. * @param sensitive sensitive flag * * @since 5.0 */ public BasicHeader(final String name, final Object value, final boolean sensitive) { super(); this.name = Args.notNull(name, "Name"); this.value = Objects.toString(value, null); this.sensitive = sensitive; } @Override public String getName() { return name; } @Override public String getValue() { return value; } @Override public boolean isSensitive() { return this.sensitive; } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append(this.getName()).append(": "); if (this.getValue() != null) { buf.append(this.getValue()); } return buf.toString(); } @Override public BasicHeader clone() throws CloneNotSupportedException { return (BasicHeader) super.clone(); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/MessageSupport.java0100664 0000000 0000000 00000016344 14403631147 026041 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.BitSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.FormattedHeader; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.MessageHeaders; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Tokenizer; /** * Support methods for HTTP message processing. * * @since 5.0 */ public class MessageSupport { /** * An empty immutable {@code String} array. */ private static final String[] EMPTY_STRING_ARRAY = {}; private MessageSupport() { // Do not allow utility class to be instantiated. } public static void formatTokens(final CharArrayBuffer dst, final String... tokens) { Args.notNull(dst, "Destination"); for (int i = 0; i < tokens.length; i++) { final String element = tokens[i]; if (i > 0) { dst.append(", "); } dst.append(element); } } public static void formatTokens(final CharArrayBuffer dst, final Set tokens) { Args.notNull(dst, "Destination"); if (tokens == null || tokens.isEmpty()) { return; } formatTokens(dst, tokens.toArray(EMPTY_STRING_ARRAY)); } public static Header format(final String name, final Set tokens) { Args.notBlank(name, "Header name"); if (tokens == null || tokens.isEmpty()) { return null; } final CharArrayBuffer buffer = new CharArrayBuffer(256); buffer.append(name); buffer.append(": "); formatTokens(buffer, tokens); return BufferedHeader.create(buffer); } public static Header format(final String name, final String... tokens) { Args.notBlank(name, "Header name"); if (tokens == null || tokens.length == 0) { return null; } final CharArrayBuffer buffer = new CharArrayBuffer(256); buffer.append(name); buffer.append(": "); formatTokens(buffer, tokens); return BufferedHeader.create(buffer); } private static final BitSet COMMA = Tokenizer.INIT_BITSET(','); public static Set parseTokens(final CharSequence src, final ParserCursor cursor) { Args.notNull(src, "Source"); Args.notNull(cursor, "Cursor"); final Set tokens = new LinkedHashSet<>(); while (!cursor.atEnd()) { final int pos = cursor.getPos(); if (src.charAt(pos) == ',') { cursor.updatePos(pos + 1); } final String token = Tokenizer.INSTANCE.parseToken(src, cursor, COMMA); if (!TextUtils.isBlank(token)) { tokens.add(token); } } return tokens; } public static Set parseTokens(final Header header) { Args.notNull(header, "Header"); if (header instanceof FormattedHeader) { final CharArrayBuffer buf = ((FormattedHeader) header).getBuffer(); final ParserCursor cursor = new ParserCursor(0, buf.length()); cursor.updatePos(((FormattedHeader) header).getValuePos()); return parseTokens(buf, cursor); } final String value = header.getValue(); final ParserCursor cursor = new ParserCursor(0, value.length()); return parseTokens(value, cursor); } public static void addContentTypeHeader(final HttpMessage message, final EntityDetails entity) { if (entity != null && entity.getContentType() != null && !message.containsHeader(HttpHeaders.CONTENT_TYPE)) { message.addHeader(new BasicHeader(HttpHeaders.CONTENT_TYPE, entity.getContentType())); } } public static void addContentEncodingHeader(final HttpMessage message, final EntityDetails entity) { if (entity != null && entity.getContentEncoding() != null && !message.containsHeader(HttpHeaders.CONTENT_ENCODING)) { message.addHeader(new BasicHeader(HttpHeaders.CONTENT_ENCODING, entity.getContentEncoding())); } } public static void addTrailerHeader(final HttpMessage message, final EntityDetails entity) { if (entity != null && !message.containsHeader(HttpHeaders.TRAILER)) { final Set trailerNames = entity.getTrailerNames(); if (trailerNames != null && !trailerNames.isEmpty()) { message.setHeader(MessageSupport.format(HttpHeaders.TRAILER, trailerNames)); } } } public static Iterator iterate(final MessageHeaders headers, final String name) { Args.notNull(headers, "Message headers"); Args.notBlank(name, "Header name"); return new BasicHeaderElementIterator(headers.headerIterator(name)); } public static HeaderElement[] parse(final Header header) { Args.notNull(header, "Headers"); final String value = header.getValue(); if (value == null) { return new HeaderElement[] {}; } final ParserCursor cursor = new ParserCursor(0, value.length()); return BasicHeaderValueParser.INSTANCE.parseElements(value, cursor); } /** * @since 5.0 */ public static boolean canResponseHaveBody(final String method, final HttpResponse response) { if (Method.HEAD.isSame(method)) { return false; } final int status = response.getCode(); if (Method.CONNECT.isSame(method) && status == HttpStatus.SC_OK) { return false; } return status >= HttpStatus.SC_SUCCESS && status != HttpStatus.SC_NO_CONTENT && status != HttpStatus.SC_NOT_MODIFIED; } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHeaderIterator.java0100664 0000000 0000000 00000011132 14245617503 026716 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.Iterator; import java.util.NoSuchElementException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.util.Args; /** * {@link java.util.Iterator} of {@link org.apache.hc.core5.http.Header}s. * * @since 4.0 */ public class BasicHeaderIterator implements Iterator
{ /** * An array of headers to iterate over. * Not all elements of this array are necessarily part of the iteration. * This array will never be modified by the iterator. * Derived implementations are expected to adhere to this restriction. */ private final Header[] allHeaders; /** * The position of the next header in {@link #allHeaders allHeaders}. * Negative if the iteration is over. */ private int currentIndex; /** * The header name to filter by. * {@code null} to iterate over all headers in the array. */ private final String headerName; /** * Creates a new header iterator. * * @param headers an array of headers over which to iterate * @param name the name of the headers over which to iterate, or * {@code null} for any */ public BasicHeaderIterator(final Header[] headers, final String name) { super(); this.allHeaders = Args.notNull(headers, "Header array"); this.headerName = name; this.currentIndex = findNext(-1); } /** * Determines the index of the next header. * * @param pos one less than the index to consider first, * -1 to search for the first header * * @return the index of the next header that matches the filter name, * or negative if there are no more headers */ private int findNext(final int pos) { int from = pos; if (from < -1) { return -1; } final int to = this.allHeaders.length-1; boolean found = false; while (!found && (from < to)) { from++; found = filterHeader(from); } return found ? from : -1; } /** * Checks whether a header is part of the iteration. * * @param index the index of the header to check * * @return {@code true} if the header should be part of the * iteration, {@code false} to skip */ private boolean filterHeader(final int index) { return (this.headerName == null) || this.headerName.equalsIgnoreCase(this.allHeaders[index].getName()); } @Override public boolean hasNext() { return this.currentIndex >= 0; } /** * Obtains the next header from this iteration. * * @return the next header in this iteration * * @throws NoSuchElementException if there are no more headers */ @Override public Header next() throws NoSuchElementException { final int current = this.currentIndex; if (current < 0) { throw new NoSuchElementException("Iteration already finished."); } this.currentIndex = findNext(current); return this.allHeaders[current]; } /** * Removing headers is not supported. * * @throws UnsupportedOperationException always */ @Override public void remove() throws UnsupportedOperationException { throw new UnsupportedOperationException("Removing headers is not supported."); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicLineFormatter.java0100664 0000000 0000000 00000007472 14245617503 026603 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Default {@link org.apache.hc.core5.http.message.LineFormatter} implementation. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class BasicLineFormatter implements LineFormatter { public final static BasicLineFormatter INSTANCE = new BasicLineFormatter(); public BasicLineFormatter() { super(); } void formatProtocolVersion(final CharArrayBuffer buffer, final ProtocolVersion version) { buffer.append(version.format()); } @Override public void formatRequestLine(final CharArrayBuffer buffer, final RequestLine reqline) { Args.notNull(buffer, "Char array buffer"); Args.notNull(reqline, "Request line"); buffer.append(reqline.getMethod()); buffer.append(' '); buffer.append(reqline.getUri()); buffer.append(' '); formatProtocolVersion(buffer, reqline.getProtocolVersion()); } @Override public void formatStatusLine(final CharArrayBuffer buffer, final StatusLine statusLine) { Args.notNull(buffer, "Char array buffer"); Args.notNull(statusLine, "Status line"); formatProtocolVersion(buffer, statusLine.getProtocolVersion()); buffer.append(' '); buffer.append(Integer.toString(statusLine.getStatusCode())); buffer.append(' '); // keep whitespace even if reason phrase is empty final String reasonPhrase = statusLine.getReasonPhrase(); if (reasonPhrase != null) { buffer.append(reasonPhrase); } } @Override public void formatHeader(final CharArrayBuffer buffer, final Header header) { Args.notNull(buffer, "Char array buffer"); Args.notNull(header, "Header"); buffer.append(header.getName()); buffer.append(": "); final String value = header.getValue(); if (value != null) { buffer.ensureCapacity(buffer.length() + value.length()); for (int valueIndex = 0; valueIndex < value.length(); valueIndex++) { char valueChar = value.charAt(valueIndex); if (valueChar == '\r' || valueChar == '\n' || valueChar == '\f' || valueChar == 0x0b) { valueChar = ' '; } buffer.append(valueChar); } } } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicListHeaderIterator.java0100664 0000000 0000000 00000011745 14245617503 027564 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; /** * {@link java.util.Iterator} of {@link org.apache.hc.core5.http.Header}s. For use by {@link HeaderGroup}. * * @since 4.0 */ class BasicListHeaderIterator implements Iterator
{ /** * A list of headers to iterate over. * Not all elements of this array are necessarily part of the iteration. */ private final List
allHeaders; /** * The position of the next header in {@link #allHeaders allHeaders}. * Negative if the iteration is over. */ private int currentIndex; /** * The position of the last returned header. * Negative if none has been returned so far. */ private int lastIndex; /** * The header name to filter by. * {@code null} to iterate over all headers in the array. */ private final String headerName; /** * Creates a new header iterator. * * @param headers a list of headers over which to iterate * @param name the name of the headers over which to iterate, or * {@code null} for any */ public BasicListHeaderIterator(final List
headers, final String name) { super(); this.allHeaders = Args.notNull(headers, "Header list"); this.headerName = name; this.currentIndex = findNext(-1); this.lastIndex = -1; } /** * Determines the index of the next header. * * @param pos one less than the index to consider first, * -1 to search for the first header * * @return the index of the next header that matches the filter name, * or negative if there are no more headers */ protected int findNext(final int pos) { int from = pos; if (from < -1) { return -1; } final int to = this.allHeaders.size()-1; boolean found = false; while (!found && (from < to)) { from++; found = filterHeader(from); } return found ? from : -1; } /** * Checks whether a header is part of the iteration. * * @param index the index of the header to check * * @return {@code true} if the header should be part of the * iteration, {@code false} to skip */ private boolean filterHeader(final int index) { if (this.headerName == null) { return true; } // non-header elements, including null, will trigger exceptions final String name = (this.allHeaders.get(index)).getName(); return this.headerName.equalsIgnoreCase(name); } @Override public boolean hasNext() { return this.currentIndex >= 0; } /** * Obtains the next header from this iteration. * * @return the next header in this iteration * * @throws NoSuchElementException if there are no more headers */ @Override public Header next() throws NoSuchElementException { final int current = this.currentIndex; if (current < 0) { throw new NoSuchElementException("Iteration already finished."); } this.lastIndex = current; this.currentIndex = findNext(current); return this.allHeaders.get(current); } /** * Removes the header that was returned last. */ @Override public void remove() throws UnsupportedOperationException { Asserts.check(this.lastIndex >= 0, "No header to remove"); this.allHeaders.remove(this.lastIndex); this.lastIndex = -1; this.currentIndex--; // adjust for the removed element } } httpcore5/src/main/java/org/apache/hc/core5/http/message/HeaderValueParser.java0100664 0000000 0000000 00000007050 14245617503 026420 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.NameValuePair; /** * Interface for parsing header values into elements. * Instances of this interface are expected to be stateless and thread-safe. * * @since 4.0 */ public interface HeaderValueParser { /** * Parses a header value into elements. * Parse errors are indicated as {@code RuntimeException}. * * @param buffer buffer holding the header value to parse * @param cursor the parser cursor containing the current position and * the bounds within the buffer for the parsing operation * * @return an array holding all elements of the header value */ HeaderElement[] parseElements(CharSequence buffer, ParserCursor cursor); /** * Parses a single header element. * A header element consist of a semicolon-separate list * of name=value definitions. * * @param buffer buffer holding the element to parse * @param cursor the parser cursor containing the current position and * the bounds within the buffer for the parsing operation * * @return the parsed element */ HeaderElement parseHeaderElement(CharSequence buffer, ParserCursor cursor); /** * Parses a list of name-value pairs. * These lists are used to specify parameters to a header element. * Parse errors are indicated as {@code ParseException}. * * @param buffer buffer holding the name-value list to parse * @param cursor the parser cursor containing the current position and * the bounds within the buffer for the parsing operation * * @return an array holding all items of the name-value list */ NameValuePair[] parseParameters(CharSequence buffer, ParserCursor cursor); /** * Parses a name=value specification, where the = and value are optional. * * @param buffer the buffer holding the name-value pair to parse * @param cursor the parser cursor containing the current position and * the bounds within the buffer for the parsing operation * * @return the name-value pair, where the value is {@code null} * if no value is specified */ NameValuePair parseNameValuePair(CharSequence buffer, ParserCursor cursor); } httpcore5/src/main/java/org/apache/hc/core5/http/message/HttpResponseWrapper.java0100664 0000000 0000000 00000004216 14403631147 027052 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.Locale; import org.apache.hc.core5.http.HttpResponse; /** * Wraps an {@link HttpResponse}. * * {@link HttpResponse} wrapper. */ public class HttpResponseWrapper extends AbstractMessageWrapper implements HttpResponse { public HttpResponseWrapper(final HttpResponse message) { super(message); } @Override public int getCode() { return getMessage().getCode(); } @Override public void setCode(final int code) { getMessage().setCode(code); } @Override public String getReasonPhrase() { return getMessage().getReasonPhrase(); } @Override public void setReasonPhrase(final String reason) { getMessage().setReasonPhrase(reason); } @Override public Locale getLocale() { return getMessage().getLocale(); } @Override public void setLocale(final Locale loc) { getMessage().setLocale(loc); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHeaderValueParser.java0100664 0000000 0000000 00000012603 14403631147 027356 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.ArrayList; import java.util.BitSet; import java.util.List; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Tokenizer; /** * Default {@link org.apache.hc.core5.http.message.HeaderValueParser} implementation. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class BasicHeaderValueParser implements HeaderValueParser { public final static BasicHeaderValueParser INSTANCE = new BasicHeaderValueParser(); private final static char PARAM_DELIMITER = ';'; private final static char ELEM_DELIMITER = ','; // IMPORTANT! // These private static variables must be treated as immutable and never exposed outside this class private static final BitSet TOKEN_DELIMITER = Tokenizer.INIT_BITSET('=', PARAM_DELIMITER, ELEM_DELIMITER); private static final BitSet VALUE_DELIMITER = Tokenizer.INIT_BITSET(PARAM_DELIMITER, ELEM_DELIMITER); private final Tokenizer tokenizer; public BasicHeaderValueParser() { this.tokenizer = Tokenizer.INSTANCE; } /** * An empty immutable {@code HeaderElement} array. */ private static final HeaderElement[] EMPTY_HEADER_ELEMENT_ARRAY = {}; /** * An empty immutable {@code NameValuePair} array. */ private static final NameValuePair[] EMPTY_NAME_VALUE_ARRAY = {}; @Override public HeaderElement[] parseElements(final CharSequence buffer, final ParserCursor cursor) { Args.notNull(buffer, "Char sequence"); Args.notNull(cursor, "Parser cursor"); final List elements = new ArrayList<>(); while (!cursor.atEnd()) { final HeaderElement element = parseHeaderElement(buffer, cursor); if (!(element.getName().isEmpty() && element.getValue() == null)) { elements.add(element); } } return elements.toArray(EMPTY_HEADER_ELEMENT_ARRAY); } @Override public HeaderElement parseHeaderElement(final CharSequence buffer, final ParserCursor cursor) { Args.notNull(buffer, "Char sequence"); Args.notNull(cursor, "Parser cursor"); final NameValuePair nvp = parseNameValuePair(buffer, cursor); NameValuePair[] params = null; if (!cursor.atEnd()) { final char ch = buffer.charAt(cursor.getPos() - 1); if (ch != ELEM_DELIMITER) { params = parseParameters(buffer, cursor); } } return new BasicHeaderElement(nvp.getName(), nvp.getValue(), params); } @Override public NameValuePair[] parseParameters(final CharSequence buffer, final ParserCursor cursor) { Args.notNull(buffer, "Char sequence"); Args.notNull(cursor, "Parser cursor"); tokenizer.skipWhiteSpace(buffer, cursor); final List params = new ArrayList<>(); while (!cursor.atEnd()) { final NameValuePair param = parseNameValuePair(buffer, cursor); params.add(param); final char ch = buffer.charAt(cursor.getPos() - 1); if (ch == ELEM_DELIMITER) { break; } } return params.toArray(EMPTY_NAME_VALUE_ARRAY); } @Override public NameValuePair parseNameValuePair(final CharSequence buffer, final ParserCursor cursor) { Args.notNull(buffer, "Char sequence"); Args.notNull(cursor, "Parser cursor"); final String name = tokenizer.parseToken(buffer, cursor, TOKEN_DELIMITER); if (cursor.atEnd()) { return new BasicNameValuePair(name, null); } final int delim = buffer.charAt(cursor.getPos()); cursor.updatePos(cursor.getPos() + 1); if (delim != '=') { return new BasicNameValuePair(name, null); } final String value = tokenizer.parseValue(buffer, cursor, VALUE_DELIMITER); if (!cursor.atEnd()) { cursor.updatePos(cursor.getPos() + 1); } return new BasicNameValuePair(name, value); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/ParserCursor.java0100664 0000000 0000000 00000003235 14245617503 025511 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.util.Tokenizer; /** * This class represents a context of a parsing operation: *
    *
  • the current position the parsing operation is expected to start at
  • *
  • the bounds limiting the scope of the parsing operation
  • *
* * @since 4.0 */ public class ParserCursor extends Tokenizer.Cursor { public ParserCursor(final int lowerBound, final int upperBound) { super(lowerBound, upperBound); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHeaderElementIterator.java0100664 0000000 0000000 00000004526 14245617503 030241 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.Iterator; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.util.Args; /** * {@link java.util.Iterator} of {@link org.apache.hc.core5.http.HeaderElement}s. * * @since 4.0 */ public class BasicHeaderElementIterator extends AbstractHeaderElementIterator { private final HeaderValueParser parser; /** * Creates a new instance of BasicHeaderElementIterator */ public BasicHeaderElementIterator( final Iterator
headerIterator, final HeaderValueParser parser) { super(headerIterator); this.parser = Args.notNull(parser, "Parser"); } public BasicHeaderElementIterator(final Iterator
headerIterator) { this(headerIterator, BasicHeaderValueParser.INSTANCE); } @Override HeaderElement parseHeaderElement(final CharSequence buf, final ParserCursor cursor) { final HeaderElement e = this.parser.parseHeaderElement(buf, cursor); if (!(e.getName().isEmpty() && e.getValue() == null)) { return e; } return null; } } httpcore5/src/main/java/org/apache/hc/core5/http/message/HttpRequestWrapper.java0100664 0000000 0000000 00000005205 14403631147 026703 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.net.URI; import java.net.URISyntaxException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.net.URIAuthority; /** * Wraps an {@link HttpRequest}. * * {@link HttpRequest} wrapper. */ public class HttpRequestWrapper extends AbstractMessageWrapper implements HttpRequest { public HttpRequestWrapper(final HttpRequest message) { super(message); } @Override public String getMethod() { return getMessage().getMethod(); } @Override public String getPath() { return getMessage().getPath(); } @Override public void setPath(final String path) { getMessage().setPath(path); } @Override public String getScheme() { return getMessage().getScheme(); } @Override public void setScheme(final String scheme) { getMessage().setScheme(scheme); } @Override public URIAuthority getAuthority() { return getMessage().getAuthority(); } @Override public void setAuthority(final URIAuthority authority) { getMessage().setAuthority(authority); } @Override public String getRequestUri() { return getMessage().getRequestUri(); } @Override public URI getUri() throws URISyntaxException { return getMessage().getUri(); } @Override public void setUri(final URI requestUri) { getMessage().setUri(requestUri); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicClassicHttpResponse.java0100664 0000000 0000000 00000006057 14245617503 027766 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.io.IOException; import java.util.Locale; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.ReasonPhraseCatalog; import org.apache.hc.core5.io.Closer; /** * Basic implementation of {@link ClassicHttpResponse}. * * @since 5.0 */ public class BasicClassicHttpResponse extends BasicHttpResponse implements ClassicHttpResponse { private static final long serialVersionUID = 1L; private HttpEntity entity; /** * Creates a new response. * * @param code the status code * @param catalog the reason phrase catalog, or * {@code null} to disable automatic * reason phrase lookup * @param locale the locale for looking up reason phrases, or * {@code null} for the system locale */ public BasicClassicHttpResponse(final int code, final ReasonPhraseCatalog catalog, final Locale locale) { super(code, catalog, locale); } /** * Creates a new response. * * @param code the status code of the response * @param reasonPhrase the reason phrase to the status code, or {@code null} */ public BasicClassicHttpResponse(final int code, final String reasonPhrase) { super(code, reasonPhrase); } /** * Creates a new response. * * @param code the status code of the response */ public BasicClassicHttpResponse(final int code) { super(code); } @Override public HttpEntity getEntity() { return this.entity; } @Override public void setEntity(final HttpEntity entity) { this.entity = entity; } @Override public void close() throws IOException { Closer.close(entity); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/AbstractHeaderElementIterator.java0100664 0000000 0000000 00000010701 14245617503 030753 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.Iterator; import java.util.NoSuchElementException; import org.apache.hc.core5.http.FormattedHeader; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.util.Args; /** * {@link java.util.Iterator} of {@link org.apache.hc.core5.http.HeaderElement}s. * * @since 5.0 */ abstract class AbstractHeaderElementIterator implements Iterator { private final Iterator
headerIt; private T currentElement; private CharSequence buffer; private ParserCursor cursor; /** * Creates a new instance of BasicHeaderElementIterator */ AbstractHeaderElementIterator(final Iterator
headerIterator) { this.headerIt = Args.notNull(headerIterator, "Header iterator"); } private void bufferHeaderValue() { this.cursor = null; this.buffer = null; while (this.headerIt.hasNext()) { final Header h = this.headerIt.next(); if (h instanceof FormattedHeader) { this.buffer = ((FormattedHeader) h).getBuffer(); this.cursor = new ParserCursor(0, this.buffer.length()); this.cursor.updatePos(((FormattedHeader) h).getValuePos()); break; } final String value = h.getValue(); if (value != null) { this.buffer = value; this.cursor = new ParserCursor(0, value.length()); break; } } } abstract T parseHeaderElement(CharSequence buf, ParserCursor cursor); private void parseNextElement() { // loop while there are headers left to parse while (this.headerIt.hasNext() || this.cursor != null) { if (this.cursor == null || this.cursor.atEnd()) { // get next header value bufferHeaderValue(); } // Anything buffered? if (this.cursor != null) { // loop while there is data in the buffer while (!this.cursor.atEnd()) { final T e = parseHeaderElement(this.buffer, this.cursor); if (e != null) { // Found something this.currentElement = e; return; } } // if at the end of the buffer if (this.cursor.atEnd()) { // discard it this.cursor = null; this.buffer = null; } } } } @Override public boolean hasNext() { if (this.currentElement == null) { parseNextElement(); } return this.currentElement != null; } @Override public T next() throws NoSuchElementException { if (this.currentElement == null) { parseNextElement(); } if (this.currentElement == null) { throw new NoSuchElementException("No more header elements available"); } final T element = this.currentElement; this.currentElement = null; return element; } @Override public void remove() throws UnsupportedOperationException { throw new UnsupportedOperationException("Remove not supported"); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/LazyLineParser.java0100664 0000000 0000000 00000004261 14245617503 025763 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Extension of {@link org.apache.hc.core5.http.message.BasicLineParser} that defers parsing of * header values. Header value is parsed only if accessed with * {@link org.apache.hc.core5.http.Header#getValue()}. *

* This parser should be used to parse request messages on the server server. *

* @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class LazyLineParser extends BasicLineParser { public final static LazyLineParser INSTANCE = new LazyLineParser(); @Override public Header parseHeader(final CharArrayBuffer buffer) throws ParseException { Args.notNull(buffer, "Char array buffer"); return new BufferedHeader(buffer, true); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/TokenParser.java0100664 0000000 0000000 00000010571 14245617503 025315 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.BitSet; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Tokenizer; /** * Low level parser for header field elements. The parsing routines of this class are designed * to produce near zero intermediate garbage and make no intermediate copies of input data. *

* This class is immutable and thread safe. * * @since 4.4 * * @deprecated Use {@link Tokenizer} */ @Deprecated @Contract(threading = ThreadingBehavior.IMMUTABLE) public class TokenParser extends Tokenizer { public static final TokenParser INSTANCE = new TokenParser(); /** Double quote */ public static final char DQUOTE = '\"'; /** Backward slash / escape character */ public static final char ESCAPE = '\\'; public String parseToken(final CharSequence buf, final ParserCursor cursor, final BitSet delimiters) { return super.parseToken(buf, cursor, delimiters); } public String parseValue(final CharSequence buf, final ParserCursor cursor, final BitSet delimiters) { return super.parseValue(buf, cursor, delimiters); } public void skipWhiteSpace(final CharSequence buf, final ParserCursor cursor) { super.skipWhiteSpace(buf, cursor); } public void copyContent(final CharSequence buf, final ParserCursor cursor, final BitSet delimiters, final StringBuilder dst) { super.copyContent(buf, cursor, delimiters, dst); } @Override public void copyContent(final CharSequence buf, final Tokenizer.Cursor cursor, final BitSet delimiters, final StringBuilder dst) { final ParserCursor parserCursor = new ParserCursor(cursor.getLowerBound(), cursor.getUpperBound()); parserCursor.updatePos(cursor.getPos()); copyContent(buf, parserCursor, delimiters, dst); cursor.updatePos(parserCursor.getPos()); } public void copyUnquotedContent(final CharSequence buf, final ParserCursor cursor, final BitSet delimiters, final StringBuilder dst) { super.copyUnquotedContent(buf, cursor, delimiters, dst); } @Override public void copyUnquotedContent(final CharSequence buf, final Tokenizer.Cursor cursor, final BitSet delimiters, final StringBuilder dst) { final ParserCursor parserCursor = new ParserCursor(cursor.getLowerBound(), cursor.getUpperBound()); parserCursor.updatePos(cursor.getPos()); copyUnquotedContent(buf, parserCursor, delimiters, dst); cursor.updatePos(parserCursor.getPos()); } public void copyQuotedContent(final CharSequence buf, final ParserCursor cursor, final StringBuilder dst) { super.copyQuotedContent(buf, cursor, dst); } @Override public void copyQuotedContent(final CharSequence buf, final Tokenizer.Cursor cursor, final StringBuilder dst) { final ParserCursor parserCursor = new ParserCursor(cursor.getLowerBound(), cursor.getUpperBound()); parserCursor.updatePos(cursor.getPos()); copyQuotedContent(buf, parserCursor, dst); cursor.updatePos(parserCursor.getPos()); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHttpResponse.java0100664 0000000 0000000 00000012636 14245617503 026464 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.Locale; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.ReasonPhraseCatalog; import org.apache.hc.core5.http.impl.EnglishReasonPhraseCatalog; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * Basic implementation of {@link HttpResponse}. * * @since 4.0 */ public class BasicHttpResponse extends HeaderGroup implements HttpResponse { private static final long serialVersionUID = 1L; private final ReasonPhraseCatalog reasonCatalog; private ProtocolVersion version; private Locale locale; private int code; private String reasonPhrase; /** * Creates a new response. * * @param code the status code * @param catalog the reason phrase catalog, or * {@code null} to disable automatic * reason phrase lookup * @param locale the locale for looking up reason phrases, or * {@code null} for the system locale */ public BasicHttpResponse( final int code, final ReasonPhraseCatalog catalog, final Locale locale) { super(); this.code = Args.positive(code, "Status code"); this.reasonCatalog = catalog != null ? catalog : EnglishReasonPhraseCatalog.INSTANCE; this.locale = locale; } /** * Creates a new response. * * @param code the status code of the response * @param reasonPhrase the reason phrase to the status code, or {@code null} */ public BasicHttpResponse(final int code, final String reasonPhrase) { this.code = Args.positive(code, "Status code"); this.reasonPhrase = reasonPhrase; this.reasonCatalog = EnglishReasonPhraseCatalog.INSTANCE; } /** * Creates a new response. * * @param code the status code of the response */ public BasicHttpResponse(final int code) { this.code = Args.positive(code, "Status code"); this.reasonPhrase = null; this.reasonCatalog = EnglishReasonPhraseCatalog.INSTANCE; } @Override public void addHeader(final String name, final Object value) { Args.notNull(name, "Header name"); addHeader(new BasicHeader(name, value)); } @Override public void setHeader(final String name, final Object value) { Args.notNull(name, "Header name"); setHeader(new BasicHeader(name, value)); } @Override public void setVersion(final ProtocolVersion version) { this.version = version; } @Override public ProtocolVersion getVersion() { return this.version; } @Override public int getCode() { return this.code; } @Override public Locale getLocale() { return this.locale; } @Override public void setCode(final int code) { Args.positive(code, "Status code"); this.code = code; this.reasonPhrase = null; } @Override public String getReasonPhrase() { return this.reasonPhrase != null ? this.reasonPhrase : getReason(this.code); } @Override public void setReasonPhrase(final String reason) { this.reasonPhrase = TextUtils.isBlank(reason) ? null : reason; } @Override public void setLocale(final Locale locale) { this.locale = Args.notNull(locale, "Locale"); } /** * Looks up a reason phrase. * This method evaluates the currently set catalog and locale. * It also handles a missing catalog. * * @param code the status code for which to look up the reason * * @return the reason phrase, or {@code null} if there is none */ protected String getReason(final int code) { return this.reasonCatalog != null ? this.reasonCatalog.getReason(code, this.locale != null ? this.locale : Locale.getDefault()) : null; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(this.code).append(' ').append(this.reasonPhrase).append(' ').append(this.version); return sb.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicTokenIterator.java0100664 0000000 0000000 00000004611 14245617503 026612 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.BitSet; import java.util.Iterator; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Tokenizer; /** * {@link java.util.Iterator} of {@link org.apache.hc.core5.http.Header} tokens.. * * @since 4.0 */ public class BasicTokenIterator extends AbstractHeaderElementIterator { private static final BitSet COMMA = Tokenizer.INIT_BITSET(','); private final Tokenizer tokenizer; /** * Creates a new instance of {@link BasicTokenIterator}. * * @param headerIterator the iterator for the headers to tokenize */ public BasicTokenIterator(final Iterator

headerIterator) { super(headerIterator); this.tokenizer = Tokenizer.INSTANCE; } @Override String parseHeaderElement(final CharSequence buf, final ParserCursor cursor) { final String token = this.tokenizer.parseToken(buf, cursor, COMMA); if (!cursor.atEnd()) { final int pos = cursor.getPos(); if (buf.charAt(pos) == ',') { cursor.updatePos(pos + 1); } } return !TextUtils.isBlank(token) ? token : null; } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHeaderValueFormatter.java0100664 0000000 0000000 00000011505 14245617503 030071 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Default {@link org.apache.hc.core5.http.message.HeaderValueFormatter} implementation. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class BasicHeaderValueFormatter implements HeaderValueFormatter { public final static BasicHeaderValueFormatter INSTANCE = new BasicHeaderValueFormatter(); private final static String SEPARATORS = " ;,:@()<>\\\"/[]?={}\t"; private final static String UNSAFE_CHARS = "\"\\"; public BasicHeaderValueFormatter() { super(); } @Override public void formatElements( final CharArrayBuffer buffer, final HeaderElement[] elems, final boolean quote) { Args.notNull(buffer, "Char array buffer"); Args.notNull(elems, "Header element array"); for (int i = 0; i < elems.length; i++) { if (i > 0) { buffer.append(", "); } formatHeaderElement(buffer, elems[i], quote); } } @Override public void formatHeaderElement( final CharArrayBuffer buffer, final HeaderElement elem, final boolean quote) { Args.notNull(buffer, "Char array buffer"); Args.notNull(elem, "Header element"); buffer.append(elem.getName()); final String value = elem.getValue(); if (value != null) { buffer.append('='); formatValue(buffer, value, quote); } final int c = elem.getParameterCount(); if (c > 0) { for (int i = 0; i < c; i++) { buffer.append("; "); formatNameValuePair(buffer, elem.getParameter(i), quote); } } } @Override public void formatParameters( final CharArrayBuffer buffer, final NameValuePair[] nvps, final boolean quote) { Args.notNull(buffer, "Char array buffer"); Args.notNull(nvps, "Header parameter array"); for (int i = 0; i < nvps.length; i++) { if (i > 0) { buffer.append("; "); } formatNameValuePair(buffer, nvps[i], quote); } } @Override public void formatNameValuePair( final CharArrayBuffer buffer, final NameValuePair nvp, final boolean quote) { Args.notNull(buffer, "Char array buffer"); Args.notNull(nvp, "Name / value pair"); buffer.append(nvp.getName()); final String value = nvp.getValue(); if (value != null) { buffer.append('='); formatValue(buffer, value, quote); } } void formatValue(final CharArrayBuffer buffer, final String value, final boolean quote) { boolean quoteFlag = quote; if (!quoteFlag) { for (int i = 0; (i < value.length()) && !quoteFlag; i++) { quoteFlag = isSeparator(value.charAt(i)); } } if (quoteFlag) { buffer.append('"'); } for (int i = 0; i < value.length(); i++) { final char ch = value.charAt(i); if (isUnsafe(ch)) { buffer.append('\\'); } buffer.append(ch); } if (quoteFlag) { buffer.append('"'); } } boolean isSeparator(final char ch) { return SEPARATORS.indexOf(ch) >= 0; } boolean isUnsafe(final char ch) { return UNSAFE_CHARS.indexOf(ch) >= 0; } } httpcore5/src/main/java/org/apache/hc/core5/http/message/LineParser.java0100664 0000000 0000000 00000005471 14245617503 025127 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.util.CharArrayBuffer; /** * Interface for parsing lines in the HEAD section of an HTTP message. * There are individual methods for parsing a request line, a * status line, or a header line. * Instances of this interface are expected to be stateless and thread-safe. * * @since 4.0 */ public interface LineParser { /** * Parses a request line from the given buffer containing one line of text. * * @param buffer a buffer holding a line to parse * * @return the parsed request line * * @throws ParseException in case of a parse error */ RequestLine parseRequestLine(CharArrayBuffer buffer) throws ParseException; /** * Parses a status line from the given buffer containing one line of text. * * @param buffer a buffer holding a line to parse * * @return the parsed status line * * @throws ParseException in case of a parse error */ StatusLine parseStatusLine(CharArrayBuffer buffer) throws ParseException; /** * Parses a header from the given buffer containing one line of text. * The full header line is expected here. Header continuation * lines must be joined by the caller before invoking this method. * * @param buffer a buffer holding the full header line. * * @return the header in the argument buffer. * * @throws ParseException in case of a parse error */ Header parseHeader(CharArrayBuffer buffer) throws ParseException; } httpcore5/src/main/java/org/apache/hc/core5/http/message/BufferedHeader.java0100664 0000000 0000000 00000010034 14245617503 025705 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.io.Serializable; import org.apache.hc.core5.http.FormattedHeader; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; import org.apache.hc.core5.util.Tokenizer; /** * This class represents a raw HTTP header whose content is parsed 'on demand' * only when the header value needs to be consumed. * * @since 4.0 */ public class BufferedHeader implements FormattedHeader, Serializable { private static final long serialVersionUID = -2768352615787625448L; /** * Header name. */ private final String name; /** * The buffer containing the entire header line. */ private final CharArrayBuffer buffer; /** * The beginning of the header value in the buffer */ private final int valuePos; /** * @since 5.0 */ public static BufferedHeader create(final CharArrayBuffer buffer) { try { return new BufferedHeader(buffer); } catch (final ParseException ex) { throw new IllegalArgumentException(ex.getMessage()); } } /** * Creates a new header from a buffer. * The name of the header will be parsed immediately, * the value only if it is accessed. * * @param buffer the buffer containing the header to represent * * @throws ParseException in case of a parse error */ public BufferedHeader(final CharArrayBuffer buffer) throws ParseException { this(buffer, true); } BufferedHeader(final CharArrayBuffer buffer, final boolean strict) throws ParseException { super(); Args.notNull(buffer, "Char array buffer"); final int colon = buffer.indexOf(':'); if (colon <= 0) { throw new ParseException("Invalid header", buffer, 0, buffer.length()); } if (strict && Tokenizer.isWhitespace(buffer.charAt(colon - 1))) { throw new ParseException("Invalid header", buffer, 0, buffer.length(), colon - 1); } final String s = buffer.substringTrimmed(0, colon); if (s.isEmpty()) { throw new ParseException("Invalid header", buffer, 0, buffer.length(), colon); } this.buffer = buffer; this.name = s; this.valuePos = colon + 1; } @Override public String getName() { return this.name; } @Override public String getValue() { return this.buffer.substringTrimmed(this.valuePos, this.buffer.length()); } @Override public boolean isSensitive() { return false; } @Override public int getValuePos() { return this.valuePos; } @Override public CharArrayBuffer getBuffer() { return this.buffer; } @Override public String toString() { return this.buffer.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/AbstractMessageWrapper.java0100664 0000000 0000000 00000007715 14403631147 027473 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.Iterator; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Args; /** * Abstract {@link HttpMessage} wrapper. * * @param A {@link HttpMessage} type. */ public abstract class AbstractMessageWrapper implements HttpMessage { private final T message; public AbstractMessageWrapper(final T message) { this.message = Args.notNull(message, "Message"); } @Override public void setVersion(final ProtocolVersion version) { message.setVersion(version); } @Override public ProtocolVersion getVersion() { return message.getVersion(); } @Override public void addHeader(final Header header) { message.addHeader(header); } @Override public void addHeader(final String name, final Object value) { message.addHeader(name, value); } @Override public void setHeader(final Header header) { message.setHeader(header); } @Override public void setHeader(final String name, final Object value) { message.setHeader(name, value); } @Override public void setHeaders(final Header... headers) { message.setHeaders(headers); } @Override public boolean removeHeader(final Header header) { return message.removeHeader(header); } @Override public boolean removeHeaders(final String name) { return message.removeHeaders(name); } @Override public boolean containsHeader(final String name) { return message.containsHeader(name); } @Override public int countHeaders(final String name) { return message.countHeaders(name); } @Override public Header[] getHeaders(final String name) { return message.getHeaders(name); } @Override public Header getHeader(final String name) throws ProtocolException { return message.getHeader(name); } @Override public Header getFirstHeader(final String name) { return message.getFirstHeader(name); } @Override public Header getLastHeader(final String name) { return message.getLastHeader(name); } T getMessage() { return message; } @Override public Header[] getHeaders() { return message.getHeaders(); } @Override public Iterator
headerIterator() { return message.headerIterator(); } @Override public Iterator
headerIterator(final String name) { return message.headerIterator(name); } @Override public String toString() { return message.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHttpRequest.java0100664 0000000 0000000 00000023446 14435411677 026324 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.net.URI; import java.net.URISyntaxException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * Basic implementation of {@link HttpRequest}. * * @since 4.0 */ public class BasicHttpRequest extends HeaderGroup implements HttpRequest { private static final long serialVersionUID = 1L; private final String method; private String path; private String scheme; private URIAuthority authority; private ProtocolVersion version; private URI requestUri; private boolean absoluteRequestUri; /** * Creates request message with the given method, host and request path. * * @param method request method. * @param scheme request scheme. * @param authority request authority. * @param path request path. * * @since 5.1 */ public BasicHttpRequest(final String method, final String scheme, final URIAuthority authority, final String path) { super(); this.method = Args.notNull(method, "Method name"); this.scheme = scheme; this.authority = authority; this.path = path; } /** * Creates request message with the given method and request path. * * @param method request method. * @param path request path. */ public BasicHttpRequest(final String method, final String path) { super(); this.method = method; if (path != null) { try { setUri(new URI(path)); } catch (final URISyntaxException ex) { this.path = path; } } } /** * Creates request message with the given method, host and request path. * * @param method request method. * @param host request host. * @param path request path. * * @since 5.0 */ public BasicHttpRequest(final String method, final HttpHost host, final String path) { super(); this.method = Args.notNull(method, "Method name"); this.scheme = host != null ? host.getSchemeName() : null; this.authority = host != null ? new URIAuthority(host) : null; this.path = path; } /** * Creates request message with the given method, request URI. * * @param method request method. * @param requestUri request URI. * * @since 5.0 */ public BasicHttpRequest(final String method, final URI requestUri) { super(); this.method = Args.notNull(method, "Method name"); setUri(Args.notNull(requestUri, "Request URI")); } /** * Creates request message with the given method and request path. * * @param method request method. * @param path request path. * * @since 5.0 */ public BasicHttpRequest(final Method method, final String path) { super(); this.method = Args.notNull(method, "Method").name(); if (path != null) { try { setUri(new URI(path)); } catch (final URISyntaxException ex) { this.path = path; } } } /** * Creates request message with the given method, host and request path. * * @param method request method. * @param host request host. * @param path request path. * * @since 5.0 */ public BasicHttpRequest(final Method method, final HttpHost host, final String path) { super(); this.method = Args.notNull(method, "Method").name(); this.scheme = host != null ? host.getSchemeName() : null; this.authority = host != null ? new URIAuthority(host) : null; this.path = path; } /** * Creates request message with the given method, request URI. * * @param method request method. * @param requestUri request URI. * * @since 5.0 */ public BasicHttpRequest(final Method method, final URI requestUri) { super(); this.method = Args.notNull(method, "Method").name(); setUri(Args.notNull(requestUri, "Request URI")); } @Override public void addHeader(final String name, final Object value) { Args.notNull(name, "Header name"); addHeader(new BasicHeader(name, value)); } @Override public void setHeader(final String name, final Object value) { Args.notNull(name, "Header name"); setHeader(new BasicHeader(name, value)); } @Override public void setVersion(final ProtocolVersion version) { this.version = version; } @Override public ProtocolVersion getVersion() { return this.version; } @Override public String getMethod() { return this.method; } @Override public String getPath() { return this.path; } @Override public void setPath(final String path) { if (path != null) { Args.check(!path.startsWith("//"), "URI path begins with multiple slashes"); } this.path = path; this.requestUri = null; } @Override public String getScheme() { return this.scheme; } @Override public void setScheme(final String scheme) { this.scheme = scheme; this.requestUri = null; } @Override public URIAuthority getAuthority() { return this.authority; } @Override public void setAuthority(final URIAuthority authority) { this.authority = authority; this.requestUri = null; } /** * Sets a flag that the {@link #getRequestUri()} method should return the request URI * in an absolute form. *

* This flag can used when the request is going to be transmitted via an HTTP/1.1 proxy. * * @since 5.1 */ public void setAbsoluteRequestUri(final boolean absoluteRequestUri) { this.absoluteRequestUri = absoluteRequestUri; } @Override public String getRequestUri() { if (absoluteRequestUri) { final StringBuilder buf = new StringBuilder(); assembleRequestUri(buf); return buf.toString(); } else { return getPath(); } } @Override public void setUri(final URI requestUri) { this.scheme = requestUri.getScheme(); if (requestUri.getHost() != null) { this.authority = new URIAuthority( requestUri.getRawUserInfo(), requestUri.getHost(), requestUri.getPort()); } else if (requestUri.getRawAuthority() != null) { try { this.authority = URIAuthority.create(requestUri.getRawAuthority()); } catch (final URISyntaxException ignore) { this.authority = null; } } else { this.authority = null; } final StringBuilder buf = new StringBuilder(); final String rawPath = requestUri.getRawPath(); if (!TextUtils.isBlank(rawPath)) { Args.check(!rawPath.startsWith("//"), "URI path begins with multiple slashes"); buf.append(rawPath); } else { buf.append("/"); } final String query = requestUri.getRawQuery(); if (query != null) { buf.append('?').append(query); } this.path = buf.toString(); this.requestUri = null; } private void assembleRequestUri(final StringBuilder buf) { if (this.authority != null) { buf.append(this.scheme != null ? this.scheme : URIScheme.HTTP.id).append("://"); buf.append(this.authority.getHostName()); if (this.authority.getPort() >= 0) { buf.append(":").append(this.authority.getPort()); } } if (this.path == null) { buf.append("/"); } else { if (buf.length() > 0 && !this.path.startsWith("/")) { buf.append("/"); } buf.append(this.path); } } @Override public URI getUri() throws URISyntaxException { if (this.requestUri == null) { final StringBuilder buf = new StringBuilder(); assembleRequestUri(buf); this.requestUri = new URI(buf.toString()); } return this.requestUri; } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append(method).append(" "); assembleRequestUri(buf); return buf.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/message/LineFormatter.java0100664 0000000 0000000 00000004653 14245617503 025637 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.util.CharArrayBuffer; /** * Interface for formatting elements of the HEAD section of an HTTP message. * There are individual methods for formatting a request line, a * status line, or a header line. The formatting methods are expected to produce * one line of formatted content that does not include a line delimiter * (such as CR-LF). Instances of this interface are expected to be stateless and * thread-safe. * * @since 4.0 */ public interface LineFormatter { /** * Formats a request line. * * @param buffer buffer to write formatted content to. * @param reqline the request line to format */ void formatRequestLine(CharArrayBuffer buffer, RequestLine reqline); /** * Formats a status line. * * @param buffer buffer to write formatted content to. * @param statline the status line to format */ void formatStatusLine(CharArrayBuffer buffer, StatusLine statline); /** * Formats a header. * * @param buffer buffer to write formatted content to. * @param header the header to format */ void formatHeader(CharArrayBuffer buffer, Header header); } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/0040775 0000000 0000000 00000000000 14435411677 022436 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/protocol/package-info.java0100664 0000000 0000000 00000002370 14245617503 025617 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core HTTP protocol interceptors. */ package org.apache.hc.core5.http.protocol; httpcore5/src/main/java/org/apache/hc/core5/http/protocol/BasicHttpContext.java0100664 0000000 0000000 00000006464 14245617503 026531 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Args; /** * Default implementation of {@link HttpContext}. *

* Please note instances of this class can be thread unsafe if the * parent context is not thread safe. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public class BasicHttpContext implements HttpContext { private final HttpContext parentContext; private final Map map; private ProtocolVersion version; public BasicHttpContext() { this(null); } public BasicHttpContext(final HttpContext parentContext) { super(); this.map = new ConcurrentHashMap<>(); this.parentContext = parentContext; } @Override public Object getAttribute(final String id) { Args.notNull(id, "Id"); Object obj = this.map.get(id); if (obj == null && this.parentContext != null) { obj = this.parentContext.getAttribute(id); } return obj; } @Override public Object setAttribute(final String id, final Object obj) { Args.notNull(id, "Id"); if (obj != null) { return this.map.put(id, obj); } return this.map.remove(id); } @Override public Object removeAttribute(final String id) { Args.notNull(id, "Id"); return this.map.remove(id); } /** * @since 5.0 */ @Override public ProtocolVersion getProtocolVersion() { return this.version != null ? this.version : HttpVersion.DEFAULT; } /** * @since 5.0 */ @Override public void setProtocolVersion(final ProtocolVersion version) { this.version = version; } /** * @since 4.2 */ public void clear() { this.map.clear(); } @Override public String toString() { return this.map.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/ResponseDate.java0100664 0000000 0000000 00000004731 14245617503 025672 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.util.Args; /** * ResponseDate is responsible for adding {@code Date} header to the * outgoing responses. This interceptor is recommended for server side protocol * processors. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.SAFE) public class ResponseDate implements HttpResponseInterceptor { public ResponseDate() { super(); } @Override public void process(final HttpResponse response, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(response, "HTTP response"); final int status = response.getCode(); if ((status >= HttpStatus.SC_OK) && !response.containsHeader(HttpHeaders.DATE)) { response.setHeader(HttpHeaders.DATE, HttpDateGenerator.INSTANCE.getCurrentDate()); } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/ResponseServer.java0100664 0000000 0000000 00000005017 14245617503 026261 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.util.Args; /** * ResponseServer is responsible for adding {@code Server} header. This * interceptor is recommended for server side protocol processors. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class ResponseServer implements HttpResponseInterceptor { private final String originServer; /** * @since 4.3 */ public ResponseServer(final String originServer) { super(); this.originServer = originServer; } public ResponseServer() { this(null); } @Override public void process(final HttpResponse response, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(response, "HTTP response"); if (!response.containsHeader(HttpHeaders.SERVER) && this.originServer != null) { response.addHeader(HttpHeaders.SERVER, this.originServer); } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriPatternType.java0100664 0000000 0000000 00000003360 14245617503 026232 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; /** * @since 5.0 */ public enum UriPatternType { REGEX, URI_PATTERN, URI_PATTERN_IN_ORDER; public static LookupRegistry newMatcher(final UriPatternType type) { if (type == null) { return new UriPatternMatcher<>(); } switch (type) { case REGEX: return new UriRegexMatcher<>(); case URI_PATTERN_IN_ORDER: return new UriPatternOrderedMatcher<>(); case URI_PATTERN: default: return new UriPatternMatcher<>(); } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/DefaultHttpProcessor.java0100664 0000000 0000000 00000011242 14245617503 027415 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import java.util.List; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseInterceptor; /** * Default immutable implementation of {@link HttpProcessor}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public final class DefaultHttpProcessor implements HttpProcessor { private final HttpRequestInterceptor[] requestInterceptors; private final HttpResponseInterceptor[] responseInterceptors; public DefaultHttpProcessor( final HttpRequestInterceptor[] requestInterceptors, final HttpResponseInterceptor[] responseInterceptors) { super(); if (requestInterceptors != null) { final int l = requestInterceptors.length; this.requestInterceptors = new HttpRequestInterceptor[l]; System.arraycopy(requestInterceptors, 0, this.requestInterceptors, 0, l); } else { this.requestInterceptors = new HttpRequestInterceptor[0]; } if (responseInterceptors != null) { final int l = responseInterceptors.length; this.responseInterceptors = new HttpResponseInterceptor[l]; System.arraycopy(responseInterceptors, 0, this.responseInterceptors, 0, l); } else { this.responseInterceptors = new HttpResponseInterceptor[0]; } } /** * @since 4.3 */ public DefaultHttpProcessor( final List requestInterceptors, final List responseInterceptors) { super(); if (requestInterceptors != null) { final int l = requestInterceptors.size(); this.requestInterceptors = requestInterceptors.toArray(new HttpRequestInterceptor[l]); } else { this.requestInterceptors = new HttpRequestInterceptor[0]; } if (responseInterceptors != null) { final int l = responseInterceptors.size(); this.responseInterceptors = responseInterceptors.toArray(new HttpResponseInterceptor[l]); } else { this.responseInterceptors = new HttpResponseInterceptor[0]; } } public DefaultHttpProcessor(final HttpRequestInterceptor... requestInterceptors) { this(requestInterceptors, null); } public DefaultHttpProcessor(final HttpResponseInterceptor... responseInterceptors) { this(null, responseInterceptors); } @Override public void process( final HttpRequest request, final EntityDetails entity, final HttpContext context) throws IOException, HttpException { for (final HttpRequestInterceptor requestInterceptor : this.requestInterceptors) { requestInterceptor.process(request, entity, context); } } @Override public void process( final HttpResponse response, final EntityDetails entity, final HttpContext context) throws IOException, HttpException { for (final HttpResponseInterceptor responseInterceptor : this.responseInterceptors) { responseInterceptor.process(response, entity, context); } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/ResponseContent.java0100664 0000000 0000000 00000012550 14245617503 026425 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.util.Args; /** * ResponseContent is the most important interceptor for outgoing responses. * It is responsible for delimiting content length by adding * {@code Content-Length} or {@code Transfer-Content} headers based * on the properties of the enclosed entity and the protocol version. * This interceptor is required for correct functioning of server side protocol * processors. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class ResponseContent implements HttpResponseInterceptor { private final boolean overwrite; /** * Default constructor. The {@code Content-Length} or {@code Transfer-Encoding} * will cause the interceptor to throw {@link ProtocolException} if already present in the * response message. */ public ResponseContent() { this(false); } /** * Constructor that can be used to fine-tune behavior of this interceptor. * * @param overwrite If set to {@code true} the {@code Content-Length} and * {@code Transfer-Encoding} headers will be created or updated if already present. * If set to {@code false} the {@code Content-Length} and * {@code Transfer-Encoding} headers will cause the interceptor to throw * {@link ProtocolException} if already present in the response message. * * @since 4.2 */ public ResponseContent(final boolean overwrite) { super(); this.overwrite = overwrite; } /** * Processes the response (possibly updating or inserting) Content-Length and Transfer-Encoding headers. * @param response The HttpResponse to modify. * @param context Unused. * @throws ProtocolException If either the Content-Length or Transfer-Encoding headers are found. * @throws IllegalArgumentException If the response is null. */ @Override public void process(final HttpResponse response, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(response, "HTTP response"); if (this.overwrite) { response.removeHeaders(HttpHeaders.TRANSFER_ENCODING); response.removeHeaders(HttpHeaders.CONTENT_LENGTH); } else { if (response.containsHeader(HttpHeaders.TRANSFER_ENCODING)) { throw new ProtocolException("Transfer-encoding header already present"); } if (response.containsHeader(HttpHeaders.CONTENT_LENGTH)) { throw new ProtocolException("Content-Length header already present"); } } final ProtocolVersion ver = context.getProtocolVersion(); if (entity != null) { final long len = entity.getContentLength(); if (len >= 0 && !entity.isChunked()) { response.addHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(entity.getContentLength())); } else if (ver.greaterEquals(HttpVersion.HTTP_1_1)) { response.addHeader(HttpHeaders.TRANSFER_ENCODING, HeaderElements.CHUNKED_ENCODING); MessageSupport.addTrailerHeader(response, entity); } MessageSupport.addContentTypeHeader(response, entity); MessageSupport.addContentEncodingHeader(response, entity); } else { final int status = response.getCode(); if (status != HttpStatus.SC_NO_CONTENT && status != HttpStatus.SC_NOT_MODIFIED) { response.addHeader(HttpHeaders.CONTENT_LENGTH, "0"); } } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriPatternOrderedMatcher.java0100664 0000000 0000000 00000010703 14245617503 030200 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Maintains a map of objects keyed by a request URI pattern. *

* Patterns may have three formats: *

*
    *
  • {@code *}
  • *
  • {@code *}
  • *
  • {@code *}
  • *
*

* This class can be used to resolve an object matching a particular request * URI. *

* * @param The type of registered objects. * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public class UriPatternOrderedMatcher implements LookupRegistry { private final Map map; public UriPatternOrderedMatcher() { super(); this.map = new LinkedHashMap<>(); } /** * Returns a {@link Set} view of the mappings contained in this matcher. * * @return a set view of the mappings contained in this matcher. * * @see Map#entrySet() * @since 4.4.9 */ public synchronized Set> entrySet() { return new HashSet<>(map.entrySet()); } /** * Registers the given object for URIs matching the given pattern. * * @param pattern the pattern to register the handler for. * @param obj the object. */ @Override public synchronized void register(final String pattern, final T obj) { Args.notNull(pattern, "URI request pattern"); this.map.put(pattern, obj); } /** * Removes registered object, if exists, for the given pattern. * * @param pattern the pattern to unregister. */ @Override public synchronized void unregister(final String pattern) { if (pattern == null) { return; } this.map.remove(pattern); } /** * Looks up an object matching the given request path. * * @param path the request path * @return object or {@code null} if no match is found. */ @Override public synchronized T lookup(final String path) { Args.notNull(path, "Request path"); for (final Entry entry : this.map.entrySet()) { final String pattern = entry.getKey(); if (path.equals(pattern)) { return entry.getValue(); } if (matchUriRequestPattern(pattern, path)) { return this.map.get(pattern); } } return null; } /** * Tests if the given request path matches the given pattern. * * @param pattern the pattern * @param path the request path * @return {@code true} if the request URI matches the pattern, {@code false} * otherwise. */ protected boolean matchUriRequestPattern(final String pattern, final String path) { if (pattern.equals("*")) { return true; } return (pattern.endsWith("*") && path.startsWith(pattern.substring(0, pattern.length() - 1))) || (pattern.startsWith("*") && path.endsWith(pattern.substring(1))); } @Override public String toString() { return this.map.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/ResponseConnControl.java0100664 0000000 0000000 00000012677 14245617503 027263 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import java.util.Iterator; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.util.Args; /** * ResponseConnControl is responsible for adding {@code Connection} header * to the outgoing responses, which is essential for managing persistence of * {@code HTTP/1.0} connections. This interceptor is recommended for * server side protocol processors. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class ResponseConnControl implements HttpResponseInterceptor { public ResponseConnControl() { super(); } @Override public void process(final HttpResponse response, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(response, "HTTP response"); Args.notNull(context, "HTTP context"); // Always drop connection after certain type of responses final int status = response.getCode(); if (status == HttpStatus.SC_BAD_REQUEST || status == HttpStatus.SC_REQUEST_TIMEOUT || status == HttpStatus.SC_LENGTH_REQUIRED || status == HttpStatus.SC_REQUEST_TOO_LONG || status == HttpStatus.SC_REQUEST_URI_TOO_LONG || status == HttpStatus.SC_SERVICE_UNAVAILABLE || status == HttpStatus.SC_NOT_IMPLEMENTED) { response.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); return; } if (!response.containsHeader(HttpHeaders.CONNECTION)) { // Always drop connection for HTTP/1.0 responses and below // if the content body cannot be correctly delimited final ProtocolVersion ver = context.getProtocolVersion(); if (entity != null && entity.getContentLength() < 0 && ver.lessEquals(HttpVersion.HTTP_1_0)) { response.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); } else { final HttpCoreContext coreContext = HttpCoreContext.adapt(context); final HttpRequest request = coreContext.getRequest(); boolean closeRequested = false; boolean keepAliveRequested = false; if (request != null) { final Iterator it = MessageSupport.iterate(request, HttpHeaders.CONNECTION); while (it.hasNext()) { final HeaderElement he = it.next(); if (he.getName().equalsIgnoreCase(HeaderElements.CLOSE)) { closeRequested = true; break; } else if (he.getName().equalsIgnoreCase(HeaderElements.KEEP_ALIVE)) { keepAliveRequested = true; } } } if (closeRequested) { response.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); } else { if (response.containsHeader(HttpHeaders.UPGRADE)) { response.addHeader(HttpHeaders.CONNECTION, HeaderElements.UPGRADE); } else { if (keepAliveRequested) { response.addHeader(HttpHeaders.CONNECTION, HeaderElements.KEEP_ALIVE); } else { if (ver.lessEquals(HttpVersion.HTTP_1_0)) { response.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); } } } } } } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/HttpProcessor.java0100664 0000000 0000000 00000004645 14245617503 026121 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponseInterceptor; /** * HTTP protocol processor is a collection of protocol interceptors that * implements the 'Chain of Responsibility' pattern, where each individual * protocol interceptor is expected to work on a particular aspect of the HTTP * protocol the interceptor is responsible for. *

* Usually the order in which interceptors are executed should not matter as * long as they do not depend on a particular state of the execution context. * If protocol interceptors have interdependencies and therefore must be * executed in a particular order, they should be added to the protocol * processor in the same sequence as their expected execution order. *

* Protocol interceptors must be implemented as thread-safe. Similarly to * servlets, protocol interceptors should not use instance variables unless * access to those variables is synchronized. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpProcessor extends HttpRequestInterceptor, HttpResponseInterceptor { } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestContent.java0100664 0000000 0000000 00000012410 14403631147 026246 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.util.Args; /** * RequestContent is the most important interceptor for outgoing requests. * It is responsible for delimiting content length by adding * {@code Content-Length} or {@code Transfer-Content} headers based * on the properties of the enclosed entity and the protocol version. * This interceptor is required for correct functioning of client side protocol * processors. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class RequestContent implements HttpRequestInterceptor { /** * Singleton instance. * @since 5.2 */ public static final HttpRequestInterceptor INSTANCE = new RequestContent(); private final boolean overwrite; /** * Default constructor. The {@code Content-Length} or {@code Transfer-Encoding} * will cause the interceptor to throw {@link ProtocolException} if already present in the * response message. */ public RequestContent() { this(false); } /** * Constructor that can be used to fine-tune behavior of this interceptor. * * @param overwrite If set to {@code true} the {@code Content-Length} and * {@code Transfer-Encoding} headers will be created or updated if already present. * If set to {@code false} the {@code Content-Length} and * {@code Transfer-Encoding} headers will cause the interceptor to throw * {@link ProtocolException} if already present in the response message. * * @since 4.2 */ public RequestContent(final boolean overwrite) { super(); this.overwrite = overwrite; } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); final String method = request.getMethod(); if (Method.TRACE.isSame(method) && entity != null) { throw new ProtocolException("TRACE request may not enclose an entity"); } if (this.overwrite) { request.removeHeaders(HttpHeaders.TRANSFER_ENCODING); request.removeHeaders(HttpHeaders.CONTENT_LENGTH); } else { if (request.containsHeader(HttpHeaders.TRANSFER_ENCODING)) { throw new ProtocolException("Transfer-encoding header already present"); } if (request.containsHeader(HttpHeaders.CONTENT_LENGTH)) { throw new ProtocolException("Content-Length header already present"); } } if (entity != null) { final ProtocolVersion ver = context.getProtocolVersion(); // Must specify a transfer encoding or a content length if (entity.isChunked() || entity.getContentLength() < 0) { if (ver.lessEquals(HttpVersion.HTTP_1_0)) { throw new ProtocolException( "Chunked transfer encoding not allowed for " + ver); } request.addHeader(HttpHeaders.TRANSFER_ENCODING, HeaderElements.CHUNKED_ENCODING); MessageSupport.addTrailerHeader(request, entity); } else { request.addHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(entity.getContentLength())); } MessageSupport.addContentTypeHeader(request, entity); MessageSupport.addContentEncodingHeader(request, entity); } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/HttpDateGenerator.java0100664 0000000 0000000 00000006527 14403631147 026663 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.util.TimeZone; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Generates a date in the format required by the HTTP protocol. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.SAFE) public class HttpDateGenerator { private static final int GRANULARITY_MILLIS = 1000; /** Date format pattern used to generate the header in RFC 1123 format. */ public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz"; /** * @deprecated This attribute is no longer supported as a part of the public API. * The time zone to use in the date header. */ @Deprecated public static final TimeZone GMT = TimeZone.getTimeZone("GMT"); public static final ZoneId GMT_ID = ZoneId.of("GMT"); /** Singleton instance. */ public static final HttpDateGenerator INSTANCE = new HttpDateGenerator(PATTERN_RFC1123, GMT_ID); private final DateTimeFormatter dateTimeFormatter; private long dateAsMillis; private String dateAsText; private ZoneId zoneId; HttpDateGenerator() { dateTimeFormatter =new DateTimeFormatterBuilder() .parseLenient() .parseCaseInsensitive() .appendPattern(PATTERN_RFC1123) .toFormatter(); zoneId = GMT_ID; } private HttpDateGenerator(final String pattern, final ZoneId zoneId) { dateTimeFormatter = new DateTimeFormatterBuilder() .parseLenient() .parseCaseInsensitive() .appendPattern(pattern) .toFormatter(); this.zoneId = zoneId; } public synchronized String getCurrentDate() { final long now = System.currentTimeMillis(); if (now - this.dateAsMillis > GRANULARITY_MILLIS) { // Generate new date string dateAsText = dateTimeFormatter.format(Instant.now().atZone(zoneId)); dateAsMillis = now; } return dateAsText; } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/HttpProcessorBuilder.java0100664 0000000 0000000 00000010776 14245617503 027432 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponseInterceptor; /** * Builder for {@link HttpProcessor} instances. * * @since 4.3 */ public class HttpProcessorBuilder { private ChainBuilder requestChainBuilder; private ChainBuilder responseChainBuilder; public static HttpProcessorBuilder create() { return new HttpProcessorBuilder(); } HttpProcessorBuilder() { super(); } private ChainBuilder getRequestChainBuilder() { if (requestChainBuilder == null) { requestChainBuilder = new ChainBuilder<>(); } return requestChainBuilder; } private ChainBuilder getResponseChainBuilder() { if (responseChainBuilder == null) { responseChainBuilder = new ChainBuilder<>(); } return responseChainBuilder; } public HttpProcessorBuilder addFirst(final HttpRequestInterceptor e) { if (e == null) { return this; } getRequestChainBuilder().addFirst(e); return this; } public HttpProcessorBuilder addLast(final HttpRequestInterceptor e) { if (e == null) { return this; } getRequestChainBuilder().addLast(e); return this; } public HttpProcessorBuilder add(final HttpRequestInterceptor e) { return addLast(e); } public HttpProcessorBuilder addAllFirst(final HttpRequestInterceptor... e) { if (e == null) { return this; } getRequestChainBuilder().addAllFirst(e); return this; } public HttpProcessorBuilder addAllLast(final HttpRequestInterceptor... e) { if (e == null) { return this; } getRequestChainBuilder().addAllLast(e); return this; } public HttpProcessorBuilder addAll(final HttpRequestInterceptor... e) { return addAllLast(e); } public HttpProcessorBuilder addFirst(final HttpResponseInterceptor e) { if (e == null) { return this; } getResponseChainBuilder().addFirst(e); return this; } public HttpProcessorBuilder addLast(final HttpResponseInterceptor e) { if (e == null) { return this; } getResponseChainBuilder().addLast(e); return this; } public HttpProcessorBuilder add(final HttpResponseInterceptor e) { return addLast(e); } public HttpProcessorBuilder addAllFirst(final HttpResponseInterceptor... e) { if (e == null) { return this; } getResponseChainBuilder().addAllFirst(e); return this; } public HttpProcessorBuilder addAllLast(final HttpResponseInterceptor... e) { if (e == null) { return this; } getResponseChainBuilder().addAllLast(e); return this; } public HttpProcessorBuilder addAll(final HttpResponseInterceptor... e) { return addAllLast(e); } public HttpProcessor build() { return new DefaultHttpProcessor( requestChainBuilder != null ? requestChainBuilder.build() : null, responseChainBuilder != null ? responseChainBuilder.build() : null); } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestHandlerRegistry.java0100664 0000000 0000000 00000012034 14403631147 027744 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestMapper; import org.apache.hc.core5.http.MisdirectedRequestException; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * Generic registry of request handlers that can be resolved by properties of request messages. * * @param request handler type. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public class RequestHandlerRegistry implements HttpRequestMapper { private final static String LOCALHOST = "localhost"; private final static String IP_127_0_0_1 = "127.0.0.1"; private final String canonicalHostName; private final Supplier> registrySupplier; private final LookupRegistry primary; private final ConcurrentMap> virtualMap; public RequestHandlerRegistry(final String canonicalHostName, final Supplier> registrySupplier) { this.canonicalHostName = TextUtils.toLowerCase(Args.notNull(canonicalHostName, "Canonical hostname")); this.registrySupplier = registrySupplier != null ? registrySupplier : UriPatternMatcher::new; this.primary = this.registrySupplier.get(); this.virtualMap = new ConcurrentHashMap<>(); } public RequestHandlerRegistry(final String canonicalHostName, final UriPatternType patternType) { this(canonicalHostName, () -> UriPatternType.newMatcher(patternType)); } public RequestHandlerRegistry(final UriPatternType patternType) { this(LOCALHOST, patternType); } public RequestHandlerRegistry() { this(LOCALHOST, UriPatternType.URI_PATTERN); } private LookupRegistry getPatternMatcher(final String hostname) { if (hostname == null || hostname.equals(canonicalHostName) || hostname.equals(LOCALHOST) || hostname.equals(IP_127_0_0_1)) { return primary; } return virtualMap.get(hostname); } @Override public T resolve(final HttpRequest request, final HttpContext context) throws MisdirectedRequestException { final URIAuthority authority = request.getAuthority(); final String key = authority != null ? TextUtils.toLowerCase(authority.getHostName()) : null; final LookupRegistry patternMatcher = getPatternMatcher(key); if (patternMatcher == null) { throw new MisdirectedRequestException("Not authoritative"); } String path = request.getPath(); final int i = path.indexOf('?'); if (i != -1) { path = path.substring(0, i); } return patternMatcher.lookup(path); } public void register(final String hostname, final String uriPattern, final T object) { Args.notBlank(uriPattern, "URI pattern"); if (object == null) { return; } final String key = TextUtils.toLowerCase(hostname); if (hostname == null || hostname.equals(canonicalHostName) || hostname.equals(LOCALHOST)) { primary.register(uriPattern, object); } else { LookupRegistry patternMatcher = virtualMap.get(key); if (patternMatcher == null) { final LookupRegistry newPatternMatcher = registrySupplier.get(); patternMatcher = virtualMap.putIfAbsent(key, newPatternMatcher); if (patternMatcher == null) { patternMatcher = newPatternMatcher; } } patternMatcher.register(uriPattern, object); } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/HttpCoreContext.java0100664 0000000 0000000 00000011442 14403631147 026364 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.util.Objects; import javax.net.ssl.SSLSession; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Args; /** * Implementation of {@link HttpContext} that provides convenience * setters for user assignable attributes and getter for readable attributes. * * @since 4.3 */ public class HttpCoreContext implements HttpContext { /** * Attribute name of a {@link EndpointDetails} object that * represents the actual connection endpoint details. */ public static final String CONNECTION_ENDPOINT = HttpContext.RESERVED_PREFIX + "connection-endpoint"; /** * Attribute name of a {@link SSLSession} object that * represents the actual connection endpoint details. */ public static final String SSL_SESSION = HttpContext.RESERVED_PREFIX + "ssl-session"; /** * Attribute name of a {@link HttpRequest} object that * represents the actual HTTP request. */ public static final String HTTP_REQUEST = HttpContext.RESERVED_PREFIX + "request"; /** * Attribute name of a {@link HttpResponse} object that * represents the actual HTTP response. */ public static final String HTTP_RESPONSE = HttpContext.RESERVED_PREFIX + "response"; public static HttpCoreContext create() { return new HttpCoreContext(); } public static HttpCoreContext adapt(final HttpContext context) { if (context == null) { return new HttpCoreContext(); } if (context instanceof HttpCoreContext) { return (HttpCoreContext) context; } return new HttpCoreContext(context); } private final HttpContext context; public HttpCoreContext(final HttpContext context) { super(); this.context = Objects.requireNonNull(context); } public HttpCoreContext() { super(); this.context = new BasicHttpContext(); } /** * @since 5.0 */ @Override public ProtocolVersion getProtocolVersion() { return this.context.getProtocolVersion(); } /** * @since 5.0 */ @Override public void setProtocolVersion(final ProtocolVersion version) { this.context.setProtocolVersion(version); } @Override public Object getAttribute(final String id) { return context.getAttribute(id); } @Override public Object setAttribute(final String id, final Object obj) { return context.setAttribute(id, obj); } @Override public Object removeAttribute(final String id) { return context.removeAttribute(id); } public T getAttribute(final String attribname, final Class clazz) { Args.notNull(clazz, "Attribute class"); final Object obj = getAttribute(attribname); if (obj == null) { return null; } return clazz.cast(obj); } /** * @since 5.0 */ public SSLSession getSSLSession() { return getAttribute(SSL_SESSION, SSLSession.class); } /** * @since 5.0 */ public EndpointDetails getEndpointDetails() { return getAttribute(CONNECTION_ENDPOINT, EndpointDetails.class); } public HttpRequest getRequest() { return getAttribute(HTTP_REQUEST, HttpRequest.class); } public HttpResponse getResponse() { return getAttribute(HTTP_RESPONSE, HttpResponse.class); } @Override public String toString() { return context.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/ChainBuilder.java0100664 0000000 0000000 00000006540 14315123013 025611 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; /** * Builder class to build a linked list (chain) of unique class instances. Each class can have * only one instance in the list. Useful for building lists of protocol interceptors. * * @see DefaultHttpProcessor * * @since 4.3 */ final class ChainBuilder { private final LinkedList list; private final Map, E> uniqueClasses; public ChainBuilder() { this.list = new LinkedList<>(); this.uniqueClasses = new HashMap<>(); } private void ensureUnique(final E e) { final E previous = this.uniqueClasses.remove(e.getClass()); if (previous != null) { this.list.remove(previous); } this.uniqueClasses.put(e.getClass(), e); } public ChainBuilder addFirst(final E e) { if (e == null) { return this; } ensureUnique(e); this.list.addFirst(e); return this; } public ChainBuilder addLast(final E e) { if (e == null) { return this; } ensureUnique(e); this.list.addLast(e); return this; } public ChainBuilder addAllFirst(final Collection c) { if (c == null) { return this; } for (final E e: c) { addFirst(e); } return this; } @SafeVarargs public final ChainBuilder addAllFirst(final E... c) { if (c == null) { return this; } for (final E e: c) { addFirst(e); } return this; } public ChainBuilder addAllLast(final Collection c) { if (c == null) { return this; } for (final E e: c) { addLast(e); } return this; } @SafeVarargs public final ChainBuilder addAllLast(final E... c) { if (c == null) { return this; } for (final E e: c) { addLast(e); } return this; } public LinkedList build() { return new LinkedList<>(this.list); } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriRegexMatcher.java0100664 0000000 0000000 00000007403 14245617503 026333 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Pattern; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Maintains a map of objects keyed by a request URI regular expression. * *

* The insertion order is in maintained in that map such that the lookup tests each regex until there is a match. This * class can be used to resolve an object matching a particular request URI. *

* * @param The type of registered objects. * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public class UriRegexMatcher implements LookupRegistry { private final Map objectMap; private final Map patternMap; public UriRegexMatcher() { super(); this.objectMap = new LinkedHashMap<>(); this.patternMap = new LinkedHashMap<>(); } /** * Registers the given object for URIs matching the given regex. * * @param regex * the regex to register the handler for. * @param obj * the object. */ @Override public synchronized void register(final String regex, final T obj) { Args.notNull(regex, "URI request regex"); this.objectMap.put(regex, obj); this.patternMap.put(regex, Pattern.compile(regex)); } /** * Removes registered object, if exists, for the given regex. * * @param regex * the regex to unregister. */ @Override public synchronized void unregister(final String regex) { if (regex == null) { return; } this.objectMap.remove(regex); this.patternMap.remove(regex); } /** * Looks up an object matching the given request path. * * @param path * the request path * @return object or {@code null} if no match is found. */ @Override public synchronized T lookup(final String path) { Args.notNull(path, "Request path"); // direct match? final T obj = this.objectMap.get(path); if (obj == null) { // regex match? for (final Entry entry : this.patternMap.entrySet()) { if (entry.getValue().matcher(path).matches()) { return objectMap.get(entry.getKey()); } } } return obj; } @Override public String toString() { return this.objectMap.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/HttpContext.java0100664 0000000 0000000 00000006236 14403631147 025560 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import org.apache.hc.core5.http.ProtocolVersion; /** * HttpContext represents execution state of an HTTP process. It is a structure * that can be used to map an attribute name to an attribute value. *

* The primary purpose of the HTTP context is to facilitate information sharing * among various logically related components. HTTP context can be used * to store a processing state for one message or several consecutive messages. * Multiple logically related messages can participate in a logical session * if the same context is reused between consecutive messages. *

*

* IMPORTANT: Please note HTTP context implementation, even when thread safe, * may not be used concurrently by multiple threads, as the context may contain * thread unsafe attributes. *

* * @since 4.0 */ public interface HttpContext { /** The prefix reserved for use by HTTP components. "http." */ String RESERVED_PREFIX = "http."; /** * Returns protocol version used in this context. * * @since 5.0 */ ProtocolVersion getProtocolVersion(); /** * Sets protocol version used in this context. * * @since 5.0 */ void setProtocolVersion(ProtocolVersion version); /** * Obtains attribute with the given name. * * @param id the attribute name. * @return attribute value, or {@code null} if not set. */ Object getAttribute(String id); /** * Sets value of the attribute with the given name. * * @param id the attribute name. * @param obj the attribute value. * @return the previous value associated with {@code id}, or * {@code null} if there was no mapping for {@code id}. */ Object setAttribute(String id, Object obj); /** * Removes attribute with the given name from the context. * * @param id the attribute name. * @return attribute value, or {@code null} if not set. */ Object removeAttribute(String id); } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestConnControl.java0100664 0000000 0000000 00000006054 14403631147 027101 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.util.Args; /** * RequestConnControl is responsible for adding {@code Connection} header * to the outgoing requests, which is essential for managing persistence of * {@code HTTP/1.0} connections. This interceptor is recommended for * client side protocol processors. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class RequestConnControl implements HttpRequestInterceptor { /** * Singleton instance. * @since 5.2 */ public static final HttpRequestInterceptor INSTANCE = new RequestConnControl(); public RequestConnControl() { super(); } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); final String method = request.getMethod(); if (Method.CONNECT.isSame(method)) { return; } if (!request.containsHeader(HttpHeaders.CONNECTION)) { // Default policy is to keep connection alive // whenever possible if (request.containsHeader(HttpHeaders.UPGRADE)) { request.addHeader(HttpHeaders.CONNECTION, HeaderElements.UPGRADE); } else { request.addHeader(HttpHeaders.CONNECTION, HeaderElements.KEEP_ALIVE); } } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestTargetHost.java0100664 0000000 0000000 00000006603 14403631147 026727 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; /** * RequestHostOutgoing is responsible for adding {@code Host} header to the outgoing message. * This interceptor is required for client side protocol processors. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class RequestTargetHost implements HttpRequestInterceptor { /** * Singleton instance. * @since 5.2 */ public static final HttpRequestInterceptor INSTANCE = new RequestTargetHost(); public RequestTargetHost() { super(); } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); Args.notNull(context, "HTTP context"); final ProtocolVersion ver = context.getProtocolVersion(); final String method = request.getMethod(); if (Method.CONNECT.isSame(method) && ver.lessEquals(HttpVersion.HTTP_1_0)) { return; } if (!request.containsHeader(HttpHeaders.HOST)) { URIAuthority authority = request.getAuthority(); if (authority == null) { if (ver.lessEquals(HttpVersion.HTTP_1_0)) { return; } throw new ProtocolException("Target host is unknown"); } if (authority.getUserInfo() != null) { authority = new URIAuthority(authority.getHostName(), authority.getPort()); } request.addHeader(HttpHeaders.HOST, authority); } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/LookupRegistry.java0100664 0000000 0000000 00000003667 14245617503 026307 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; /** * A lookup registry. * * @param The type of objects to register and lookup. */ public interface LookupRegistry { /** * Registers the given object for URIs matching the given pattern. * * @param pattern the pattern to register the handler for. * @param obj the object. */ void register(String pattern, T obj); /** * Looks up an object matching the given request path. * * @param value the request path * @return object or {@code null} if no match is found. */ T lookup(String value); /** * Removes registered object, if exists, for the given pattern. * * @param pattern the pattern to unregister. */ void unregister(String pattern); } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestDate.java0100664 0000000 0000000 00000004752 14403631147 025523 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.util.Args; /** * RequestDate interceptor is responsible for adding {@code Date} header * to the outgoing requests This interceptor is optional for client side * protocol processors. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.SAFE) public class RequestDate implements HttpRequestInterceptor { /** * Singleton instance. * @since 5.2 */ public static final HttpRequestInterceptor INSTANCE = new RequestDate(); public RequestDate() { super(); } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); if (entity != null && !request.containsHeader(HttpHeaders.DATE)) { request.setHeader(HttpHeaders.DATE, HttpDateGenerator.INSTANCE.getCurrentDate()); } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriPatternMatcher.java0100664 0000000 0000000 00000011541 14245617503 026674 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Maintains a map of objects keyed by a request URI pattern. *

* Patterns may have three formats: *

*
    *
  • {@code *}
  • *
  • {@code *}
  • *
  • {@code *}
  • *
*

* This class can be used to resolve an object matching a particular request * URI. *

* * @param The type of registered objects. * @since 4.0 */ @Contract(threading = ThreadingBehavior.SAFE) public class UriPatternMatcher implements LookupRegistry { private final Map map; public UriPatternMatcher() { super(); this.map = new LinkedHashMap<>(); } /** * Returns a {@link Set} view of the mappings contained in this matcher. * * @return a set view of the mappings contained in this matcher. * * @see Map#entrySet() * @since 4.4.9 */ public synchronized Set> entrySet() { return new HashSet<>(map.entrySet()); } /** * Registers the given object for URIs matching the given pattern. * * @param pattern * the pattern to register the handler for. * @param obj * the object. */ @Override public synchronized void register(final String pattern, final T obj) { Args.notNull(pattern, "URI request pattern"); this.map.put(pattern, obj); } /** * Removes registered object, if exists, for the given pattern. * * @param pattern * the pattern to unregister. */ @Override public synchronized void unregister(final String pattern) { if (pattern == null) { return; } this.map.remove(pattern); } /** * Looks up an object matching the given request path. * * @param path * the request path * @return object or {@code null} if no match is found. */ @Override public synchronized T lookup(final String path) { Args.notNull(path, "Request path"); // direct match? T obj = this.map.get(path); if (obj == null) { // pattern match? String bestMatch = null; for (final String pattern : this.map.keySet()) { if (matchUriRequestPattern(pattern, path)) { // we have a match. is it any better? if (bestMatch == null || (bestMatch.length() < pattern.length()) || (bestMatch.length() == pattern.length() && pattern.endsWith("*"))) { obj = this.map.get(pattern); bestMatch = pattern; } } } } return obj; } /** * Tests if the given request path matches the given pattern. * * @param pattern * the pattern * @param path * the request path * @return {@code true} if the request URI matches the pattern, {@code false} otherwise. */ protected boolean matchUriRequestPattern(final String pattern, final String path) { if (pattern.equals("*")) { return true; } return (pattern.endsWith("*") && path.startsWith(pattern.substring(0, pattern.length() - 1))) || (pattern.startsWith("*") && path.endsWith(pattern.substring(1))); } @Override public String toString() { return this.map.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestValidateHost.java0100664 0000000 0000000 00000006267 14245617503 027244 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import java.net.URISyntaxException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; /** * RequestTargetHost is responsible for copying {@code Host} header value to * {@link HttpRequest#setAuthority(URIAuthority)} of the incoming message. * This interceptor is required for server side protocol processors. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class RequestValidateHost implements HttpRequestInterceptor { public RequestValidateHost() { super(); } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); final Header header = request.getHeader(HttpHeaders.HOST); if (header != null) { final URIAuthority authority; try { authority = URIAuthority.create(header.getValue()); } catch (final URISyntaxException ex) { throw new ProtocolException(ex.getMessage(), ex); } request.setAuthority(authority); } else { final ProtocolVersion version = request.getVersion() != null ? request.getVersion() : HttpVersion.HTTP_1_1; if (version.greaterEquals(HttpVersion.HTTP_1_1)) { throw new ProtocolException("Host header is absent"); } } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestExpectContinue.java0100664 0000000 0000000 00000005740 14403631147 027601 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Args; /** * RequestExpectContinue is responsible for enabling the 'expect-continue' * handshake by adding {@code Expect} header. This interceptor is * recommended for client side protocol processors. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class RequestExpectContinue implements HttpRequestInterceptor { /** * Singleton instance. * * @since 5.2 */ public static final RequestExpectContinue INSTANCE = new RequestExpectContinue(); public RequestExpectContinue() { super(); } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); if (!request.containsHeader(HttpHeaders.EXPECT)) { if (entity != null) { final ProtocolVersion ver = context.getProtocolVersion(); // Do not send the expect header if request body is known to be empty if (entity.getContentLength() != 0 && !ver.lessEquals(HttpVersion.HTTP_1_0)) { request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE); } } } } } httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestUserAgent.java0100664 0000000 0000000 00000005162 14403631147 026537 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.util.Args; /** * RequestUserAgent is responsible for adding {@code User-Agent} header. * This interceptor is recommended for client side protocol processors. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class RequestUserAgent implements HttpRequestInterceptor { /** * Singleton instance. * @since 5.2 */ public static final HttpRequestInterceptor INSTANCE = new RequestUserAgent(); private final String userAgent; public RequestUserAgent(final String userAgent) { super(); this.userAgent = userAgent; } public RequestUserAgent() { this(null); } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); if (!request.containsHeader(HttpHeaders.USER_AGENT) && this.userAgent != null) { request.addHeader(HttpHeaders.USER_AGENT, this.userAgent); } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/0040775 0000000 0000000 00000000000 14403631147 021351 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/package-info.java0100664 0000000 0000000 00000003726 14245617503 024551 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core HTTP transport APIs based on the asynchronous, event driven I/O model. *

* The application programming interface is based on the concept * of channels and event handlers. The channels act as conduits * for asynchronous data output. They are generally expected to * be thread-safe and could be used by multiple threads concurrently. * The event handlers react to asynchronous signals or events and * communicate with the opposite endpoint through available channels. * Event handlers can be specialized as data producers, * data consumers or can be both. Generally event handlers can only * be used by a single thread at a time and do not require synchronization * as long as they do not interact with event handlers run by separate * threads. *

*/ package org.apache.hc.core5.http.nio; httpcore5/src/main/java/org/apache/hc/core5/http/nio/DataStreamChannel.java0100664 0000000 0000000 00000005431 14245617503 025536 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.Header; /** * Abstract byte stream channel *

* Implementations are expected to be thread-safe. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public interface DataStreamChannel extends StreamChannel { /** * Signals intent by the data producer to produce more data. * Once the channel is able to accept data its handler is expected * to trigger an event to notify the data producer. */ void requestOutput(); /** * Writes data from the buffer through this channel into the underlying byte stream. * If the underlying byte stream is temporarily unable to accept more data * it can return zero to indicate that no data could be written to the data * stream. The data producer can choose to call {@link #requestOutput()} * to signal its intent to produce more data. * * @param src source of data * * @return The number of bytes written, possibly zero */ @Override int write(ByteBuffer src) throws IOException; /** * Terminates the underlying data stream and optionally writes * a closing sequence with the given trailers. *

* Please note that some data streams may not support trailers * and may silently ignore the trailers parameter. *

*/ void endStream(List trailers) throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataExchangeHandler.java0100664 0000000 0000000 00000003101 14245617503 027020 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; /** * Abstract asynchronous data exchange handler that acts as a data consumer * and a data producer. * * @since 5.0 */ public interface AsyncDataExchangeHandler extends AsyncDataConsumer, AsyncDataProducer { /** * Triggered to signal a failure in data processing. * * @param cause the cause of the failure. */ void failed(Exception cause); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentEncoder.java0100664 0000000 0000000 00000004475 14245617503 025141 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.http.Header; /** * Abstract HTTP content encoder. HTTP content encoders can be used * to apply the required coding transformation and write entity * content to the underlying channel in small chunks. * * @since 4.0 */ public interface ContentEncoder { /** * Writes a portion of entity content to the underlying channel. * * @param src The buffer from which content is to be retrieved * @return The number of bytes read, possibly zero * @throws IOException if I/O error occurs while writing content */ int write(ByteBuffer src) throws IOException; /** * Terminates the content stream. * * @throws IOException if I/O error occurs while writing content */ void complete(List trailers) throws IOException; /** * Returns {@code true} if the entity has been transferred in its * entirety. * * @return {@code true} if all the content has been produced, * {@code false} otherwise. */ boolean isCompleted(); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/SessionOutputBuffer.java0100664 0000000 0000000 00000007004 14245617503 026214 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.charset.CharacterCodingException; import org.apache.hc.core5.util.CharArrayBuffer; /** * Session output buffer for non-blocking HTTP/1.1 connections. *

* This interface facilitates intermediate buffering of output data streamed out * to a destination channel and provides provides methods for writing lines of text. *

* * @since 4.0 */ public interface SessionOutputBuffer { /** * Determines if the buffer contains data. * * @return {@code true} if there is data in the buffer, * {@code false} otherwise. */ boolean hasData(); /** * Returns available space in the buffer. * * @return available space. */ int capacity(); /** * Returns the length of this buffer. * * @return buffer length. */ int length(); /** * Makes an attempt to flush the content of this buffer to the given * destination {@link WritableByteChannel}. * * @param channel the destination channel. * @return The number of bytes written, possibly zero. * @throws IOException in case of an I/O error. */ int flush(WritableByteChannel channel) throws IOException; /** * Copies content of the source buffer into this buffer. The capacity of * the destination will be expanded in order to accommodate the entire * content of the source buffer. * * @param src the source buffer. */ void write(ByteBuffer src); /** * Reads a sequence of bytes from the source channel into this buffer. * * @param src the source channel. */ void write(ReadableByteChannel src) throws IOException; /** * Copies content of the source buffer into this buffer as one line of text * including a line delimiter. The capacity of the destination will be * expanded in order to accommodate the entire content of the source buffer. *

* The choice of a char encoding and line delimiter sequence is up to the * specific implementations of this interface. * * @param src the source buffer. */ void writeLine(CharArrayBuffer src) throws CharacterCodingException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncPushProducer.java0100664 0000000 0000000 00000004315 14245617503 025641 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract asynchronous push response producer. * * @since 5.0 */ public interface AsyncPushProducer extends AsyncDataProducer { /** * Triggered to signal the ability of the underlying response channel * to accept response messages. The data producer can choose to send * a final response message immediately inside the call or asynchronously * at some later point. The final response can be preceded by a number * of intermediate messages. * * @param channel the response channel capable to accepting response messages. * @param context the actual execution context. */ void produceResponse(ResponseChannel channel, HttpContext context) throws HttpException, IOException; /** * Triggered to signal a failure in data generation. * * @param cause the cause of the failure. */ void failed(Exception cause); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/NHttpMessageWriter.java0100664 0000000 0000000 00000004000 14245617503 025746 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.MessageHeaders; /** * Message writer intended to serialize HTTP message head to a session buffer. * * @since 4.0 */ public interface NHttpMessageWriter { /** * Resets the writer. The writer will be ready to start serializing another * HTTP message. */ void reset(); /** * Writes out the HTTP message head. * * @param message HTTP message. * @param buffer session output buffer. * @throws IOException in case of an I/O error. * @throws HttpException in case the HTTP message is malformed or * violates the HTTP protocol. */ void write(T message, SessionOutputBuffer buffer) throws IOException, HttpException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/FileContentDecoder.java0100664 0000000 0000000 00000004654 14245617503 025726 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import java.nio.channels.FileChannel; /** * A content decoder capable of transferring data directly to a {@link FileChannel} * * @since 4.0 */ public interface FileContentDecoder extends ContentDecoder { /** * Transfers a portion of entity content from the underlying network channel * into the given file channel. *

* Warning: Many implementations cannot write beyond the length of the file. * If the position exceeds the channel's size, some implementations * may throw an IOException. *

* * @param dst the target FileChannel to transfer data into. * @param position * The position within the file at which the transfer is to begin; * must be non-negative. * Must be less than or equal to the size of the file * @param count * The maximum number of bytes to be transferred; must be * non-negative * @throws IOException if some I/O error occurs. * @return The number of bytes, possibly zero, * that were actually transferred */ long transfer(FileChannel dst, long position, long count) throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/0040775 0000000 0000000 00000000000 14403631147 023065 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/package-info.java0100664 0000000 0000000 00000002412 14245617503 026254 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Support classes for the asynchronous I/O model. */ package org.apache.hc.core5.http.nio.support; httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java0100664 0000000 0000000 00000010054 14245617503 032307 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncResponseProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * {@link AsyncServerExchangeHandler} implementation that immediately responds * with a predefined response generated by a {@link AsyncResponseProducer} and * ignores any entity content enclosed in the request message. * * @since 5.0 */ public final class ImmediateResponseExchangeHandler implements AsyncServerExchangeHandler { private final AsyncResponseProducer responseProducer; public ImmediateResponseExchangeHandler(final AsyncResponseProducer responseProducer) { this.responseProducer = Args.notNull(responseProducer, "Response producer"); } public ImmediateResponseExchangeHandler(final HttpResponse response, final String message) { this(new BasicResponseProducer(response, AsyncEntityProducers.create(message))); } public ImmediateResponseExchangeHandler(final int status, final String message) { this(new BasicHttpResponse(status), message); } @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { responseProducer.sendResponse(responseChannel, context); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(Integer.MAX_VALUE); } @Override public void consume(final ByteBuffer src) throws IOException { } @Override public void streamEnd(final List trailers) throws HttpException, IOException { } @Override public int available() { return responseProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { responseProducer.produce(channel); } @Override public void failed(final Exception cause) { responseProducer.failed(cause); releaseResources(); } @Override public void releaseResources() { responseProducer.releaseResources(); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicRequestConsumer.java0100664 0000000 0000000 00000012015 14403631147 030032 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Basic implementation of {@link AsyncRequestConsumer} that represents the request message as * a {@link Message} and relies on a {@link AsyncEntityConsumer} to process request entity stream. * * @since 5.0 */ public class BasicRequestConsumer implements AsyncRequestConsumer> { private final Supplier> dataConsumerSupplier; private final AtomicReference> dataConsumerRef; public BasicRequestConsumer(final Supplier> dataConsumerSupplier) { this.dataConsumerSupplier = Args.notNull(dataConsumerSupplier, "Data consumer supplier"); this.dataConsumerRef = new AtomicReference<>(); } public BasicRequestConsumer(final AsyncEntityConsumer dataConsumer) { this(() -> dataConsumer); } @Override public void consumeRequest( final HttpRequest request, final EntityDetails entityDetails, final HttpContext httpContext, final FutureCallback> resultCallback) throws HttpException, IOException { Args.notNull(request, "Request"); if (entityDetails != null) { final AsyncEntityConsumer dataConsumer = dataConsumerSupplier.get(); if (dataConsumer == null) { throw new HttpException("Supplied data consumer is null"); } dataConsumerRef.set(dataConsumer); dataConsumer.streamStart(entityDetails, new CallbackContribution(resultCallback) { @Override public void completed(final T body) { final Message result = new Message<>(request, body); if (resultCallback != null) { resultCallback.completed(result); } } }); } else { final Message result = new Message<>(request, null); if (resultCallback != null) { resultCallback.completed(result); } } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); dataConsumer.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); dataConsumer.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); dataConsumer.streamEnd(trailers); } @Override public void failed(final Exception cause) { releaseResources(); } @Override public void releaseResources() { final AsyncEntityConsumer dataConsumer = dataConsumerRef.getAndSet(null); if (dataConsumer != null) { dataConsumer.releaseResources(); } } } ././@LongLink0100644 0000000 0000000 00000000157 14245617503 011645 Lustar 0000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncServerFilterChainExchangeHandlerFactory.javahttpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncServerFilterChainExchangeHandlerFa0100664 0000000 0000000 00000017174 14245617503 032610 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncFilterChain; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.AsyncResponseProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; /** * Factory for {@link AsyncServerExchangeHandler} instances that delegate request processing * to a {@link AsyncServerFilterChainElement}. * * @since 5.0 */ public final class AsyncServerFilterChainExchangeHandlerFactory implements HandlerFactory { private final AsyncServerFilterChainElement filterChain; private final Callback exceptionCallback; public AsyncServerFilterChainExchangeHandlerFactory(final AsyncServerFilterChainElement filterChain, final Callback exceptionCallback) { this.filterChain = Args.notNull(filterChain, "Filter chain"); this.exceptionCallback = exceptionCallback; } public AsyncServerFilterChainExchangeHandlerFactory(final AsyncServerFilterChainElement filterChain) { this(filterChain, null); } @Override public AsyncServerExchangeHandler create(final HttpRequest request, final HttpContext context) throws HttpException { return new AsyncServerExchangeHandler() { private final AtomicReference dataConsumerRef = new AtomicReference<>(); private final AtomicReference responseProducerRef = new AtomicReference<>(); @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { dataConsumerRef.set(filterChain.handle(request, entityDetails, context, new AsyncFilterChain.ResponseTrigger() { @Override public void sendInformation( final HttpResponse response) throws HttpException, IOException { responseChannel.sendInformation(response, context); } @Override public void submitResponse( final HttpResponse response, final AsyncEntityProducer entityProducer) throws HttpException, IOException { final AsyncResponseProducer responseProducer = new BasicResponseProducer(response, entityProducer); responseProducerRef.set(responseProducer); responseProducer.sendResponse(responseChannel, context); } @Override public void pushPromise(final HttpRequest promise, final AsyncPushProducer responseProducer) throws HttpException, IOException { responseChannel.pushPromise(promise, responseProducer, context); } })); } @Override public void failed(final Exception cause) { if (exceptionCallback != null) { exceptionCallback.execute(cause); } final AsyncResponseProducer handler = responseProducerRef.get(); if (handler != null) { handler.failed(cause); } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { final AsyncDataConsumer dataConsumer = dataConsumerRef.get(); if (dataConsumer != null) { dataConsumer.updateCapacity(capacityChannel); } else { capacityChannel.update(Integer.MAX_VALUE); } } @Override public void consume(final ByteBuffer src) throws IOException { final AsyncDataConsumer dataConsumer = dataConsumerRef.get(); if (dataConsumer != null) { dataConsumer.consume(src); } } @Override public void streamEnd(final List trailers) throws HttpException, IOException { final AsyncDataConsumer dataConsumer = dataConsumerRef.get(); if (dataConsumer != null) { dataConsumer.streamEnd(trailers); } } @Override public int available() { final AsyncResponseProducer responseProducer = responseProducerRef.get(); Asserts.notNull(responseProducer, "Response producer"); return responseProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { final AsyncResponseProducer responseProducer = responseProducerRef.get(); Asserts.notNull(responseProducer, "Response producer"); responseProducer.produce(channel); } @Override public void releaseResources() { final AsyncDataConsumer dataConsumer = dataConsumerRef.getAndSet(null); if (dataConsumer != null) { dataConsumer.releaseResources(); } final AsyncResponseProducer responseProducer = responseProducerRef.getAndSet(null); if (responseProducer != null) { responseProducer.releaseResources(); } } }; } } ././@LongLink0100644 0000000 0000000 00000000155 14245617503 011643 Lustar 0000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFactory.javahttpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFact0100664 0000000 0000000 00000007327 14245617503 032662 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestMapper; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.MisdirectedRequestException; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Factory for {@link AsyncServerExchangeHandler} instances that make use * of {@link HttpRequestMapper} to dispatch * the request to a particular {@link AsyncServerExchangeHandler} for processing. * * @since 5.0 */ public final class DefaultAsyncResponseExchangeHandlerFactory implements HandlerFactory { private final HttpRequestMapper> mapper; private final Decorator decorator; public DefaultAsyncResponseExchangeHandlerFactory( final HttpRequestMapper> mapper, final Decorator decorator) { this.mapper = Args.notNull(mapper, "Request handler mapper"); this.decorator = decorator; } public DefaultAsyncResponseExchangeHandlerFactory(final HttpRequestMapper> mapper) { this(mapper, null); } private AsyncServerExchangeHandler createHandler(final HttpRequest request, final HttpContext context) throws HttpException { try { final Supplier supplier = mapper.resolve(request, context); return supplier != null ? supplier.get() : new ImmediateResponseExchangeHandler(HttpStatus.SC_NOT_FOUND, "Resource not found"); } catch (final MisdirectedRequestException ex) { return new ImmediateResponseExchangeHandler(HttpStatus.SC_MISDIRECTED_REQUEST, "Not authoritative"); } } @Override public AsyncServerExchangeHandler create(final HttpRequest request, final HttpContext context) throws HttpException { final AsyncServerExchangeHandler handler = createHandler(request, context); if (handler != null) { return decorator != null ? decorator.decorate(handler) : handler; } return null; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicRequestProducer.java0100664 0000000 0000000 00000011161 14245617503 030027 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.net.URI; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.protocol.HttpContext; /** * Basic implementation of {@link AsyncRequestProducer} that produces one fixed request * and relies on a {@link AsyncEntityProducer} to generate request entity stream. * * @since 5.0 */ public class BasicRequestProducer implements AsyncRequestProducer { private final HttpRequest request; private final AsyncEntityProducer dataProducer; public BasicRequestProducer(final HttpRequest request, final AsyncEntityProducer dataProducer) { this.request = request; this.dataProducer = dataProducer; } public BasicRequestProducer(final String method, final HttpHost host, final String path, final AsyncEntityProducer dataProducer) { this(new BasicHttpRequest(method, host, path), dataProducer); } public BasicRequestProducer(final String method, final HttpHost host, final String path) { this(method, host, path, null); } public BasicRequestProducer(final String method, final URI requestUri, final AsyncEntityProducer dataProducer) { this(new BasicHttpRequest(method, requestUri), dataProducer); } public BasicRequestProducer(final String method, final URI requestUri) { this(method, requestUri, null); } public BasicRequestProducer(final Method method, final HttpHost host, final String path, final AsyncEntityProducer dataProducer) { this(new BasicHttpRequest(method, host, path), dataProducer); } public BasicRequestProducer(final Method method, final HttpHost host, final String path) { this(method, host, path, null); } public BasicRequestProducer(final Method method, final URI requestUri, final AsyncEntityProducer dataProducer) { this(new BasicHttpRequest(method, requestUri), dataProducer); } public BasicRequestProducer(final Method method, final URI requestUri) { this(method, requestUri, null); } @Override public void sendRequest(final RequestChannel requestChannel, final HttpContext httpContext) throws HttpException, IOException { requestChannel.sendRequest(request, dataProducer, httpContext); } @Override public int available() { return dataProducer != null ? dataProducer.available() : 0; } @Override public void produce(final DataStreamChannel channel) throws IOException { if (dataProducer != null) { dataProducer.produce(channel); } } @Override public boolean isRepeatable() { return dataProducer == null || dataProducer.isRepeatable(); } @Override public void failed(final Exception cause) { try { if (dataProducer != null) { dataProducer.failed(cause); } } finally { releaseResources(); } } @Override public void releaseResources() { if (dataProducer != null) { dataProducer.releaseResources(); } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncPushBuilder.java0100664 0000000 0000000 00000011561 14403631147 027155 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer; import org.apache.hc.core5.http.support.AbstractResponseBuilder; import org.apache.hc.core5.util.Args; import java.util.Arrays; /** * Builder for {@link AsyncPushProducer} instances. * * @since 5.0 */ public class AsyncPushBuilder extends AbstractResponseBuilder { private AsyncEntityProducer entityProducer; AsyncPushBuilder(final int status) { super(status); } public static AsyncPushBuilder create(final int status) { Args.checkRange(status, 100, 599, "HTTP status code"); return new AsyncPushBuilder(status); } @Override public AsyncPushBuilder setVersion(final ProtocolVersion version) { super.setVersion(version); return this; } @Override public AsyncPushBuilder setHeaders(final Header... headers) { super.setHeaders(headers); return this; } @Override public AsyncPushBuilder addHeader(final Header header) { super.addHeader(header); return this; } @Override public AsyncPushBuilder addHeader(final String name, final String value) { super.addHeader(name, value); return this; } @Override public AsyncPushBuilder removeHeader(final Header header) { super.removeHeader(header); return this; } @Override public AsyncPushBuilder removeHeaders(final String name) { super.removeHeaders(name); return this; } @Override public AsyncPushBuilder setHeader(final Header header) { super.setHeader(header); return this; } @Override public AsyncPushBuilder setHeader(final String name, final String value) { super.setHeader(name, value); return this; } public AsyncEntityProducer getEntity() { return entityProducer; } public AsyncPushBuilder setEntity(final AsyncEntityProducer entityProducer) { this.entityProducer = entityProducer; return this; } public AsyncPushBuilder setEntity(final String content, final ContentType contentType) { this.entityProducer = new BasicAsyncEntityProducer(content, contentType); return this; } public AsyncPushBuilder setEntity(final String content) { this.entityProducer = new BasicAsyncEntityProducer(content); return this; } public AsyncPushBuilder setEntity(final byte[] content, final ContentType contentType) { this.entityProducer = new BasicAsyncEntityProducer(content, contentType); return this; } @Override public AsyncPushProducer build() { final HttpResponse response = new BasicHttpResponse(getStatus()); response.setVersion(getVersion()); response.setHeaders(getHeaders()); return new BasicPushProducer(response, entityProducer); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("AsyncPushProducer [status="); builder.append(getStatus()); builder.append(", headerGroup="); builder.append(Arrays.toString(getHeaders())); builder.append(", entity="); builder.append(entityProducer != null ? entityProducer.getClass() : null); builder.append("]"); return builder.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/0040775 0000000 0000000 00000000000 14435411677 024517 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/package-info.java0100664 0000000 0000000 00000002517 14245617503 027703 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Support classes for the asynchronous I/O model that emulate * behavior of the classic (blocking) I/O model. */ package org.apache.hc.core5.http.nio.support.classic; httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/AbstractSharedBuffer.java0100664 0000000 0000000 00000006225 14245617503 031403 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.impl.nio.ExpandableBuffer; import org.apache.hc.core5.util.Args; /** * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) abstract class AbstractSharedBuffer extends ExpandableBuffer { final ReentrantLock lock; final Condition condition; volatile boolean endStream; volatile boolean aborted; public AbstractSharedBuffer(final ReentrantLock lock, final int initialBufferSize) { super(initialBufferSize); this.lock = Args.notNull(lock, "Lock"); this.condition = lock.newCondition(); } @Override public boolean hasData() { lock.lock(); try { return super.hasData(); } finally { lock.unlock(); } } @Override public int capacity() { lock.lock(); try { return super.capacity(); } finally { lock.unlock(); } } @Override public int length() { lock.lock(); try { return super.length(); } finally { lock.unlock(); } } public void abort() { lock.lock(); try { endStream = true; aborted = true; condition.signalAll(); } finally { lock.unlock(); } } public void reset() { if (aborted) { return; } lock.lock(); try { setInputMode(); buffer().clear(); endStream = false; } finally { lock.unlock(); } } public boolean isEndStream() { lock.lock(); try { return endStream && !super.hasData(); } finally { lock.unlock(); } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/ContentInputStream.java0100664 0000000 0000000 00000004676 14245617503 031175 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.util.Args; /** * {@link InputStream} adaptor for {@link ContentInputBuffer}. * * @since 4.0 */ public class ContentInputStream extends InputStream { private final ContentInputBuffer buffer; public ContentInputStream(final ContentInputBuffer buffer) { super(); Args.notNull(buffer, "Input buffer"); this.buffer = buffer; } @Override public int available() throws IOException { return this.buffer.length(); } @Override public int read(final byte[] b, final int off, final int len) throws IOException { return this.buffer.read(b, off, len); } @Override public int read(final byte[] b) throws IOException { if (b == null) { return 0; } return this.buffer.read(b, 0, b.length); } @Override public int read() throws IOException { return this.buffer.read(); } @Override public void close() throws IOException { // read and discard the remainder of the message final byte[] tmp = new byte[1024]; while (this.buffer.read(tmp, 0, tmp.length) >= 0) { } super.close(); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/ContentOutputStream.java0100664 0000000 0000000 00000004402 14245617503 031361 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.io.IOException; import java.io.OutputStream; import org.apache.hc.core5.util.Args; /** * {@link OutputStream} adaptor for {@link ContentOutputBuffer}. * * @since 4.0 */ public class ContentOutputStream extends OutputStream { private final ContentOutputBuffer buffer; public ContentOutputStream(final ContentOutputBuffer buffer) { super(); Args.notNull(buffer, "Output buffer"); this.buffer = buffer; } @Override public void close() throws IOException { this.buffer.writeCompleted(); } @Override public void flush() throws IOException { } @Override public void write(final byte[] b, final int off, final int len) throws IOException { this.buffer.write(b, off, len); } @Override public void write(final byte[] b) throws IOException { if (b == null) { return; } this.buffer.write(b, 0, b.length); } @Override public void write(final int b) throws IOException { this.buffer.write(b); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/ContentOutputBuffer.java0100664 0000000 0000000 00000005144 14245617503 031343 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.io.IOException; /** * Generic content output buffer. * * @since 4.0 */ public interface ContentOutputBuffer { /** * Return length data stored in the buffer * * @return data length */ int length(); /** * Resets the buffer by clearing its state and stored content. */ void reset(); /** * Writes {@code len} bytes from the specified byte array * starting at offset {@code off} to this buffer. *

* If {@code off} is negative, or {@code len} is negative, or * {@code off+len} is greater than the length of the array * {@code b}, this method can throw a runtime exception. The exact type * of runtime exception thrown by this method depends on implementation. * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @throws IOException if an I/O error occurs. */ void write(byte[] b, int off, int len) throws IOException; /** * Writes the specified byte to this buffer. * * @param b the {@code byte}. * @throws IOException if an I/O error occurs. */ void write(int b) throws IOException; /** * Indicates the content has been fully written. * @throws IOException if an I/O error occurs. */ void writeCompleted() throws IOException; } ././@LongLink0100644 0000000 0000000 00000000157 14435411677 011652 Lustar 0000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/AbstractClassicServerExchangeHandler.javahttpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/AbstractClassicServerExchangeHa0100664 0000000 0000000 00000024755 14435411677 032624 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.message.HttpResponseWrapper; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; /** * {@link AsyncServerExchangeHandler} implementation that acts as a compatibility * layer for classic {@link InputStream} / {@link OutputStream} based interfaces. * Blocking input / output processing is executed through an {@link Executor}. * * @since 5.0 */ public abstract class AbstractClassicServerExchangeHandler implements AsyncServerExchangeHandler { private enum State { IDLE, ACTIVE, COMPLETED } private final int initialBufferSize; private final Executor executor; private final AtomicReference state; private final AtomicReference exception; private volatile SharedInputBuffer inputBuffer; private volatile SharedOutputBuffer outputBuffer; public AbstractClassicServerExchangeHandler(final int initialBufferSize, final Executor executor) { this.initialBufferSize = Args.positive(initialBufferSize, "Initial buffer size"); this.executor = Args.notNull(executor, "Executor"); this.exception = new AtomicReference<>(); this.state = new AtomicReference<>(State.IDLE); } /** * Handles an incoming request optionally reading its entity content form the given input stream * and generates a response optionally writing out its entity content into the given output stream. * * @param request the incoming request * @param requestStream the request stream if the request encloses an entity, * {@code null} otherwise. * @param response the outgoing response. * @param responseStream the response entity output stream. * @param context the actual execution context. */ protected abstract void handle( HttpRequest request, InputStream requestStream, HttpResponse response, OutputStream responseStream, HttpContext context) throws IOException, HttpException; public Exception getException() { return exception.get(); } @Override public final void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final AtomicBoolean responseCommitted = new AtomicBoolean(false); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); final HttpResponse responseWrapper = new HttpResponseWrapper(response){ private void ensureNotCommitted() { Asserts.check(!responseCommitted.get(), "Response already committed"); } @Override public void addHeader(final String name, final Object value) { ensureNotCommitted(); super.addHeader(name, value); } @Override public void setHeader(final String name, final Object value) { ensureNotCommitted(); super.setHeader(name, value); } @Override public void setVersion(final ProtocolVersion version) { ensureNotCommitted(); super.setVersion(version); } @Override public void setCode(final int code) { ensureNotCommitted(); super.setCode(code); } @Override public void setReasonPhrase(final String reason) { ensureNotCommitted(); super.setReasonPhrase(reason); } @Override public void setLocale(final Locale locale) { ensureNotCommitted(); super.setLocale(locale); } }; final InputStream inputStream; if (entityDetails != null) { inputBuffer = new SharedInputBuffer(initialBufferSize); inputStream = new ContentInputStream(inputBuffer); } else { inputStream = null; } outputBuffer = new SharedOutputBuffer(initialBufferSize); final OutputStream outputStream = new ContentOutputStream(outputBuffer) { private void triggerResponse() throws IOException { try { if (responseCommitted.compareAndSet(false, true)) { responseChannel.sendResponse(response, new EntityDetails() { @Override public long getContentLength() { return -1; } @Override public String getContentType() { final Header h = response.getFirstHeader(HttpHeaders.CONTENT_TYPE); return h != null ? h.getValue() : null; } @Override public String getContentEncoding() { final Header h = response.getFirstHeader(HttpHeaders.CONTENT_ENCODING); return h != null ? h.getValue() : null; } @Override public boolean isChunked() { return false; } @Override public Set getTrailerNames() { return null; } }, context); } } catch (final HttpException ex) { throw new IOException(ex.getMessage(), ex); } } @Override public void close() throws IOException { triggerResponse(); super.close(); } @Override public void write(final byte[] b, final int off, final int len) throws IOException { triggerResponse(); super.write(b, off, len); } @Override public void write(final byte[] b) throws IOException { triggerResponse(); super.write(b); } @Override public void write(final int b) throws IOException { triggerResponse(); super.write(b); } }; if (state.compareAndSet(State.IDLE, State.ACTIVE)) { executor.execute(() -> { try { handle(request, inputStream, responseWrapper, outputStream, context); Closer.close(inputStream); outputStream.close(); } catch (final Exception ex) { exception.compareAndSet(null, ex); if (inputBuffer != null) { inputBuffer.abort(); } outputBuffer.abort(); } finally { state.set(State.COMPLETED); } }); } } @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { if (inputBuffer != null) { inputBuffer.updateCapacity(capacityChannel); } } @Override public final void consume(final ByteBuffer src) throws IOException { Asserts.notNull(inputBuffer, "Input buffer"); inputBuffer.fill(src); } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { Asserts.notNull(inputBuffer, "Input buffer"); inputBuffer.markEndStream(); } @Override public final int available() { Asserts.notNull(outputBuffer, "Output buffer"); return outputBuffer.length(); } @Override public final void produce(final DataStreamChannel channel) throws IOException { Asserts.notNull(outputBuffer, "Output buffer"); outputBuffer.flush(channel); } @Override public final void failed(final Exception cause) { exception.compareAndSet(null, cause); releaseResources(); } @Override public void releaseResources() { } } ././@LongLink0100644 0000000 0000000 00000000150 14403631147 011632 Lustar 0000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/AbstractClassicEntityProducer.javahttpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/AbstractClassicEntityProducer.j0100664 0000000 0000000 00000010521 14403631147 032623 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.io.IOException; import java.io.OutputStream; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.util.Args; /** * {@link AsyncEntityProducer} implementation that acts as a compatibility * layer for classic {@link OutputStream} based interfaces. Blocking output * processing is executed through an {@link Executor}. * * @since 5.0 */ public abstract class AbstractClassicEntityProducer implements AsyncEntityProducer { private enum State { IDLE, ACTIVE, COMPLETED } private final SharedOutputBuffer buffer; private final ContentType contentType; private final Executor executor; private final AtomicReference state; private final AtomicReference exception; public AbstractClassicEntityProducer(final int initialBufferSize, final ContentType contentType, final Executor executor) { this.buffer = new SharedOutputBuffer(initialBufferSize); this.contentType = contentType; this.executor = Args.notNull(executor, "Executor"); this.state = new AtomicReference<>(State.IDLE); this.exception = new AtomicReference<>(); } /** * Writes out entity data into the given stream. * * @param contentType the entity content type * @param outputStream the output stream */ protected abstract void produceData(ContentType contentType, OutputStream outputStream) throws IOException; @Override public final boolean isRepeatable() { return false; } @Override public final int available() { return buffer.length(); } @Override public final void produce(final DataStreamChannel channel) throws IOException { if (state.compareAndSet(State.IDLE, State.ACTIVE)) { executor.execute(() -> { try { produceData(contentType, new ContentOutputStream(buffer)); buffer.writeCompleted(); } catch (final Exception ex) { buffer.abort(); } finally { state.set(State.COMPLETED); } }); } buffer.flush(channel); } @Override public final long getContentLength() { return -1; } @Override public final String getContentType() { return contentType != null ? contentType.toString() : null; } @Override public String getContentEncoding() { return null; } @Override public final boolean isChunked() { return false; } @Override public final Set getTrailerNames() { return null; } @Override public final void failed(final Exception cause) { if (exception.compareAndSet(null, cause)) { releaseResources(); } } public final Exception getException() { return exception.get(); } @Override public void releaseResources() { } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/SharedOutputBuffer.java0100664 0000000 0000000 00000013115 14245617503 031134 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.ByteBuffer; import java.util.concurrent.locks.ReentrantLock; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.nio.DataStreamChannel; /** * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public final class SharedOutputBuffer extends AbstractSharedBuffer implements ContentOutputBuffer { private volatile DataStreamChannel dataStreamChannel; private volatile boolean hasCapacity; private volatile boolean endStreamPropagated; public SharedOutputBuffer(final ReentrantLock lock, final int initialBufferSize) { super(lock, initialBufferSize); this.hasCapacity = false; this.endStreamPropagated = false; } public SharedOutputBuffer(final int bufferSize) { this(new ReentrantLock(), bufferSize); } public void flush(final DataStreamChannel channel) throws IOException { lock.lock(); try { dataStreamChannel = channel; hasCapacity = true; setOutputMode(); if (buffer().hasRemaining()) { dataStreamChannel.write(buffer()); } if (!buffer().hasRemaining() && endStream) { propagateEndStream(); } condition.signalAll(); } finally { lock.unlock(); } } private void ensureNotAborted() throws InterruptedIOException { if (aborted) { throw new InterruptedIOException("Operation aborted"); } } @Override public void write(final byte[] b, final int off, final int len) throws IOException { final ByteBuffer src = ByteBuffer.wrap(b, off, len); lock.lock(); try { ensureNotAborted(); setInputMode(); while (src.hasRemaining()) { // always buffer small chunks if (src.remaining() < 1024 && buffer().remaining() > src.remaining()) { buffer().put(src); } else { if (buffer().position() > 0 || dataStreamChannel == null) { waitFlush(); } if (buffer().position() == 0 && dataStreamChannel != null) { final int bytesWritten = dataStreamChannel.write(src); if (bytesWritten == 0) { hasCapacity = false; waitFlush(); } } } } } finally { lock.unlock(); } } @Override public void write(final int b) throws IOException { lock.lock(); try { ensureNotAborted(); setInputMode(); if (!buffer().hasRemaining()) { waitFlush(); } buffer().put((byte)b); } finally { lock.unlock(); } } @Override public void writeCompleted() throws IOException { if (endStream) { return; } lock.lock(); try { if (!endStream) { endStream = true; if (dataStreamChannel != null) { setOutputMode(); if (buffer().hasRemaining()) { dataStreamChannel.requestOutput(); } else { propagateEndStream(); } } } } finally { lock.unlock(); } } private void waitFlush() throws InterruptedIOException { setOutputMode(); if (dataStreamChannel != null) { dataStreamChannel.requestOutput(); } ensureNotAborted(); while (buffer().hasRemaining() || !hasCapacity) { try { condition.await(); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); throw new InterruptedIOException(ex.getMessage()); } ensureNotAborted(); } setInputMode(); } private void propagateEndStream() throws IOException { if (!endStreamPropagated) { dataStreamChannel.endStream(); endStreamPropagated = true; } } } ././@LongLink0100644 0000000 0000000 00000000150 14403631147 011632 Lustar 0000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/AbstractClassicEntityConsumer.javahttpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/AbstractClassicEntityConsumer.j0100664 0000000 0000000 00000012003 14403631147 032630 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.UnsupportedCharsetException; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.util.Args; /** * {@link AsyncEntityConsumer} implementation that acts as a compatibility * layer for classic {@link InputStream} based interfaces. Blocking input * processing is executed through an {@link Executor}. * * @param entity representation. * * @since 5.0 */ public abstract class AbstractClassicEntityConsumer implements AsyncEntityConsumer { private enum State { IDLE, ACTIVE, COMPLETED } private final Executor executor; private final SharedInputBuffer buffer; private final AtomicReference state; private final AtomicReference resultRef; private final AtomicReference exceptionRef; public AbstractClassicEntityConsumer(final int initialBufferSize, final Executor executor) { this.executor = Args.notNull(executor, "Executor"); this.buffer = new SharedInputBuffer(initialBufferSize); this.state = new AtomicReference<>(State.IDLE); this.resultRef = new AtomicReference<>(); this.exceptionRef = new AtomicReference<>(); } /** * Processes entity data from the given stream. * * @param contentType the entity content type * @param inputStream the input stream * @return the result of entity processing. */ protected abstract T consumeData(ContentType contentType, InputStream inputStream) throws IOException; @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { buffer.updateCapacity(capacityChannel); } @Override public final void streamStart(final EntityDetails entityDetails, final FutureCallback resultCallback) throws HttpException, IOException { final ContentType contentType; try { contentType = ContentType.parse(entityDetails.getContentType()); } catch (final UnsupportedCharsetException ex) { throw new UnsupportedEncodingException(ex.getMessage()); } if (state.compareAndSet(State.IDLE, State.ACTIVE)) { executor.execute(() -> { try { final T result = consumeData(contentType, new ContentInputStream(buffer)); resultRef.set(result); resultCallback.completed(result); } catch (final Exception ex) { buffer.abort(); resultCallback.failed(ex); } finally { state.set(State.COMPLETED); } }); } } @Override public final void consume(final ByteBuffer src) throws IOException { buffer.fill(src); } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { buffer.markEndStream(); } @Override public final void failed(final Exception cause) { if (exceptionRef.compareAndSet(null, cause)) { releaseResources(); } } public final Exception getException() { return exceptionRef.get(); } @Override public final T getContent() { return resultRef.get(); } @Override public void releaseResources() { } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/ContentInputBuffer.java0100664 0000000 0000000 00000006031 14245617503 031136 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.io.IOException; /** * Generic content input buffer. * * @since 4.0 */ public interface ContentInputBuffer { /** * Return length data stored in the buffer * * @return data length */ int length(); /** * Resets the buffer by clearing its state and stored content. */ void reset(); /** * Reads up to {@code len} bytes of data from this buffer into * an array of bytes. The exact number of bytes read depends how many bytes * are stored in the buffer. * *

If {@code off} is negative, or {@code len} is negative, or * {@code off+len} is greater than the length of the array * {@code b}, this method can throw a runtime exception. The exact type * of runtime exception thrown by this method depends on implementation. * This method returns {@code -1} if the end of content stream has been * reached. * * @param b the buffer into which the data is read. * @param off the start offset in array {@code b} * at which the data is written. * @param len the maximum number of bytes to read. * @return the total number of bytes read into the buffer, or * {@code -1} if there is no more data because the end of * the stream has been reached. * @throws IOException if an I/O error occurs. */ int read(byte[] b, int off, int len) throws IOException; /** * Reads one byte from this buffer. If the buffer is empty this method can * throw a runtime exception. The exact type of runtime exception thrown * by this method depends on implementation. This method returns * {@code -1} if the end of content stream has been reached. * * @return one byte */ int read() throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/classic/SharedInputBuffer.java0100664 0000000 0000000 00000012552 14325247343 030737 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.nio.CapacityChannel; /** * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public final class SharedInputBuffer extends AbstractSharedBuffer implements ContentInputBuffer { private final int initialBufferSize; private final AtomicInteger capacityIncrement; private volatile CapacityChannel capacityChannel; public SharedInputBuffer(final ReentrantLock lock, final int initialBufferSize) { super(lock, initialBufferSize); this.initialBufferSize = initialBufferSize; this.capacityIncrement = new AtomicInteger(0); } public SharedInputBuffer(final int bufferSize) { this(new ReentrantLock(), bufferSize); } public int fill(final ByteBuffer src) { lock.lock(); try { setInputMode(); ensureAdjustedCapacity(buffer().position() + src.remaining()); buffer().put(src); final int remaining = buffer().remaining(); condition.signalAll(); return remaining; } finally { lock.unlock(); } } private void incrementCapacity() throws IOException { if (capacityChannel != null) { final int increment = capacityIncrement.getAndSet(0); if (increment > 0) { capacityChannel.update(increment); } } } public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { lock.lock(); try { this.capacityChannel = capacityChannel; setInputMode(); if (buffer().position() == 0) { capacityChannel.update(initialBufferSize); } } finally { lock.unlock(); } } private void awaitInput() throws InterruptedIOException { if (!buffer().hasRemaining()) { setInputMode(); while (buffer().position() == 0 && !endStream && !aborted) { try { condition.await(); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); throw new InterruptedIOException(ex.getMessage()); } } setOutputMode(); } } @Override public int read() throws IOException { lock.lock(); try { setOutputMode(); awaitInput(); if (aborted) { return -1; } if (!buffer().hasRemaining() && endStream) { return -1; } final int b = buffer().get() & 0xff; capacityIncrement.incrementAndGet(); if (!buffer().hasRemaining()) { incrementCapacity(); } return b; } finally { lock.unlock(); } } @Override public int read(final byte[] b, final int off, final int len) throws IOException { lock.lock(); try { setOutputMode(); awaitInput(); if (aborted) { return -1; } if (!buffer().hasRemaining() && endStream) { return -1; } final int chunk = Math.min(buffer().remaining(), len); buffer().get(b, off, chunk); capacityIncrement.addAndGet(chunk); if (!buffer().hasRemaining()) { incrementCapacity(); } return chunk; } finally { lock.unlock(); } } public void markEndStream() { if (endStream) { return; } lock.lock(); try { if (!endStream) { endStream = true; capacityChannel = null; condition.signalAll(); } } finally { lock.unlock(); } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicServerExchangeHandler.java0100664 0000000 0000000 00000005301 14245617503 031101 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Basic {@link AbstractServerExchangeHandler} implementation that delegates * request processing and response generation to a {@link AsyncServerRequestHandler}. * * @since 5.0 */ public class BasicServerExchangeHandler extends AbstractServerExchangeHandler { private final AsyncServerRequestHandler requestHandler; public BasicServerExchangeHandler(final AsyncServerRequestHandler requestHandler) { super(); this.requestHandler = Args.notNull(requestHandler, "Response handler"); } @Override protected AsyncRequestConsumer supplyConsumer( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) throws HttpException { return requestHandler.prepare(request, entityDetails, context); } @Override protected void handle( final T requestMessage, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { requestHandler.handle(requestMessage, responseTrigger, context); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractAsyncResponseConsumer.java0100664 0000000 0000000 00000014110 14403631147 031716 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.UnsupportedCharsetException; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Abstract asynchronous response consumer that makes use of {@link AsyncEntityConsumer} * to process response message content. * * @param response processing result representation. * @param response entity representation. * * @since 5.0 */ public abstract class AbstractAsyncResponseConsumer implements AsyncResponseConsumer { private final Supplier> dataConsumerSupplier; private final AtomicReference> dataConsumerRef; public AbstractAsyncResponseConsumer(final Supplier> dataConsumerSupplier) { this.dataConsumerSupplier = Args.notNull(dataConsumerSupplier, "Data consumer supplier"); this.dataConsumerRef = new AtomicReference<>(); } public AbstractAsyncResponseConsumer(final AsyncEntityConsumer dataConsumer) { this(() -> dataConsumer); } /** * Triggered to generate object that represents a result of response message processing. * @param response the response message. * @param entity the response entity. * @param contentType the response content type. * @return the result of response processing. */ protected abstract T buildResult(HttpResponse response, E entity, ContentType contentType); @Override public final void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext httpContext, final FutureCallback resultCallback) throws HttpException, IOException { if (entityDetails != null) { final AsyncEntityConsumer dataConsumer = dataConsumerSupplier.get(); if (dataConsumer == null) { throw new HttpException("Supplied data consumer is null"); } dataConsumerRef.set(dataConsumer); dataConsumer.streamStart(entityDetails, new CallbackContribution(resultCallback) { @Override public void completed(final E entity) { final ContentType contentType; try { contentType = ContentType.parse(entityDetails.getContentType()); final T result = buildResult(response, entity, contentType); if (resultCallback != null) { resultCallback.completed(result); } } catch (final UnsupportedCharsetException ex) { if (resultCallback != null) { resultCallback.failed(ex); } } } }); } else { final T result = buildResult(response, null, null); if (resultCallback != null) { resultCallback.completed(result); } } } @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); if (dataConsumer != null) { dataConsumer.updateCapacity(capacityChannel); } else { capacityChannel.update(Integer.MAX_VALUE); } } @Override public final void consume(final ByteBuffer src) throws IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); if (dataConsumer != null) { dataConsumer.consume(src); } } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); if (dataConsumer != null) { dataConsumer.streamEnd(trailers); } } @Override public final void failed(final Exception cause) { releaseResources(); } @Override public final void releaseResources() { final AsyncEntityConsumer dataConsumer = dataConsumerRef.getAndSet(null); if (dataConsumer != null) { dataConsumer.releaseResources(); } } }httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicClientExchangeHandler.java0100664 0000000 0000000 00000016135 14245617503 031060 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Basic {@link AsyncClientExchangeHandler} implementation that makes use * of {@link AsyncRequestProducer} to generate request message * and {@link AsyncResponseConsumer} to process the response message returned by the server. * * @since 5.0 */ public final class BasicClientExchangeHandler implements AsyncClientExchangeHandler { private final AsyncRequestProducer requestProducer; private final AsyncResponseConsumer responseConsumer; private final AtomicBoolean completed; private final FutureCallback resultCallback; private final AtomicBoolean outputTerminated; public BasicClientExchangeHandler( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final FutureCallback resultCallback) { this.requestProducer = Args.notNull(requestProducer, "Request producer"); this.responseConsumer = Args.notNull(responseConsumer, "Response consumer"); this.completed = new AtomicBoolean(false); this.resultCallback = resultCallback; this.outputTerminated = new AtomicBoolean(false); } @Override public void produceRequest(final RequestChannel requestChannel, final HttpContext httpContext) throws HttpException, IOException { requestProducer.sendRequest(requestChannel, httpContext); } @Override public int available() { return requestProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { if (outputTerminated.get()) { channel.endStream(); return; } requestProducer.produce(channel); } @Override public void consumeInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { responseConsumer.informationResponse(response, httpContext); } @Override public void consumeResponse(final HttpResponse response, final EntityDetails entityDetails, final HttpContext httpContext) throws HttpException, IOException { if (response.getCode() >= HttpStatus.SC_CLIENT_ERROR) { outputTerminated.set(true); requestProducer.releaseResources(); } responseConsumer.consumeResponse(response, entityDetails, httpContext, new FutureCallback() { @Override public void completed(final T result) { if (completed.compareAndSet(false, true)) { try { if (resultCallback != null) { resultCallback.completed(result); } } finally { internalReleaseResources(); } } } @Override public void failed(final Exception ex) { if (completed.compareAndSet(false, true)) { try { if (resultCallback != null) { resultCallback.failed(ex); } } finally { internalReleaseResources(); } } } @Override public void cancelled() { if (completed.compareAndSet(false, true)) { try { if (resultCallback != null) { resultCallback.cancelled(); } } finally { internalReleaseResources(); } } } }); } @Override public void cancel() { if (completed.compareAndSet(false, true)) { try { if (resultCallback != null) { resultCallback.cancelled(); } } finally { internalReleaseResources(); } } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { responseConsumer.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { responseConsumer.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { responseConsumer.streamEnd(trailers); } @Override public void failed(final Exception cause) { try { requestProducer.failed(cause); responseConsumer.failed(cause); } finally { if (completed.compareAndSet(false, true)) { try { if (resultCallback != null) { resultCallback.failed(cause); } } finally { internalReleaseResources(); } } } } private void internalReleaseResources() { requestProducer.releaseResources(); responseConsumer.releaseResources(); } @Override public void releaseResources() { } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncServerFilterChainElement.java0100664 0000000 0000000 00000005270 14403631147 031620 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncFilterChain; import org.apache.hc.core5.http.nio.AsyncFilterHandler; import org.apache.hc.core5.http.protocol.HttpContext; /** * An element in an asynchronous request processing chain. * * @since 5.0 */ public final class AsyncServerFilterChainElement { private final AsyncFilterHandler handler; private final AsyncServerFilterChainElement next; private final AsyncFilterChain filterChain; public AsyncServerFilterChainElement(final AsyncFilterHandler handler, final AsyncServerFilterChainElement next) { this.handler = handler; this.next = next; this.filterChain = next != null ? next::handle : null; } public AsyncDataConsumer handle( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context, final AsyncFilterChain.ResponseTrigger responseTrigger) throws HttpException, IOException { return handler.handle(request, entityDetails, context, responseTrigger, filterChain); } @Override public String toString() { return "{" + "handler=" + handler.getClass() + ", next=" + (next != null ? next.handler.getClass() : "null") + '}'; } }httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractAsyncPushHandler.java0100664 0000000 0000000 00000011045 14245617503 030631 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Abstract push response handler. * * @since 5.0 * * @param response message representation. */ public abstract class AbstractAsyncPushHandler implements AsyncPushConsumer { private final AsyncResponseConsumer responseConsumer; public AbstractAsyncPushHandler(final AsyncResponseConsumer responseConsumer) { this.responseConsumer = Args.notNull(responseConsumer, "Response consumer"); } /** * Triggered to handle the push message with the given promised request. * * @param promise the promised request message. * @param responseMessage the pushed response message. */ protected abstract void handleResponse( final HttpRequest promise, final T responseMessage) throws IOException, HttpException; /** * Triggered to handle the exception thrown while processing a push response. * * @param promise the promised request message. * @param cause the cause of error. */ protected void handleError(final HttpRequest promise, final Exception cause) { } @Override public final void consumePromise( final HttpRequest promise, final HttpResponse response, final EntityDetails entityDetails, final HttpContext httpContext) throws HttpException, IOException { responseConsumer.consumeResponse(response, entityDetails, httpContext, new FutureCallback() { @Override public void completed(final T result) { try { handleResponse(promise, result); } catch (final Exception ex) { failed(ex); } } @Override public void failed(final Exception cause) { handleError(promise, cause); releaseResources(); } @Override public void cancelled() { releaseResources(); } }); } @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { responseConsumer.updateCapacity(capacityChannel); } @Override public final void consume(final ByteBuffer src) throws IOException { responseConsumer.consume(src); } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { responseConsumer.streamEnd(trailers); } @Override public final void failed(final Exception cause) { responseConsumer.failed(cause); releaseResources(); } @Override public final void releaseResources() { if (responseConsumer != null) { responseConsumer.releaseResources(); } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicResponseProducer.java0100664 0000000 0000000 00000010523 14245617503 030176 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncResponseProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Basic implementation of {@link AsyncResponseProducer} that produces one fixed response * and relies on a {@link AsyncEntityProducer} to generate response entity stream. * * @since 5.0 */ public class BasicResponseProducer implements AsyncResponseProducer { private final HttpResponse response; private final AsyncEntityProducer dataProducer; public BasicResponseProducer(final HttpResponse response, final AsyncEntityProducer dataProducer) { this.response = Args.notNull(response, "Response"); this.dataProducer = dataProducer; } public BasicResponseProducer(final HttpResponse response) { this.response = Args.notNull(response, "Response"); this.dataProducer = null; } public BasicResponseProducer(final int code, final AsyncEntityProducer dataProducer) { this(new BasicHttpResponse(code), dataProducer); } public BasicResponseProducer(final HttpResponse response, final String message, final ContentType contentType) { this(response, AsyncEntityProducers.create(message, contentType)); } public BasicResponseProducer(final HttpResponse response, final String message) { this(response, message, ContentType.TEXT_PLAIN); } public BasicResponseProducer(final int code, final String message, final ContentType contentType) { this(new BasicHttpResponse(code), message, contentType); } public BasicResponseProducer(final int code, final String message) { this(new BasicHttpResponse(code), message); } public BasicResponseProducer(final AsyncEntityProducer dataProducer) { this(HttpStatus.SC_OK, dataProducer); } @Override public void sendResponse(final ResponseChannel responseChannel, final HttpContext httpContext) throws HttpException, IOException { responseChannel.sendResponse(response, dataProducer, httpContext); } @Override public int available() { return dataProducer != null ? dataProducer.available() : 0; } @Override public void produce(final DataStreamChannel channel) throws IOException { if (dataProducer != null) { dataProducer.produce(channel); } } @Override public void failed(final Exception cause) { if (dataProducer != null) { dataProducer.failed(cause); } releaseResources(); } @Override public void releaseResources() { if (dataProducer != null) { dataProducer.releaseResources(); } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncResponseBuilder.java0100664 0000000 0000000 00000011640 14403631147 030032 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.util.Arrays; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncResponseProducer; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer; import org.apache.hc.core5.http.support.AbstractResponseBuilder; import org.apache.hc.core5.util.Args; /** * Builder for {@link AsyncResponseProducer} instances. * * @since 5.0 */ public class AsyncResponseBuilder extends AbstractResponseBuilder { private AsyncEntityProducer entityProducer; AsyncResponseBuilder(final int status) { super(status); } public static AsyncResponseBuilder create(final int status) { Args.checkRange(status, 100, 599, "HTTP status code"); return new AsyncResponseBuilder(status); } @Override public AsyncResponseBuilder setVersion(final ProtocolVersion version) { super.setVersion(version); return this; } @Override public AsyncResponseBuilder setHeaders(final Header... headers) { super.setHeaders(headers); return this; } @Override public AsyncResponseBuilder addHeader(final Header header) { super.addHeader(header); return this; } @Override public AsyncResponseBuilder addHeader(final String name, final String value) { super.addHeader(name, value); return this; } @Override public AsyncResponseBuilder removeHeader(final Header header) { super.removeHeader(header); return this; } @Override public AsyncResponseBuilder removeHeaders(final String name) { super.removeHeaders(name); return this; } @Override public AsyncResponseBuilder setHeader(final Header header) { super.setHeader(header); return this; } @Override public AsyncResponseBuilder setHeader(final String name, final String value) { super.setHeader(name, value); return this; } public AsyncEntityProducer getEntity() { return entityProducer; } public AsyncResponseBuilder setEntity(final AsyncEntityProducer entityProducer) { this.entityProducer = entityProducer; return this; } public AsyncResponseBuilder setEntity(final String content, final ContentType contentType) { this.entityProducer = new BasicAsyncEntityProducer(content, contentType); return this; } public AsyncResponseBuilder setEntity(final String content) { this.entityProducer = new BasicAsyncEntityProducer(content); return this; } public AsyncResponseBuilder setEntity(final byte[] content, final ContentType contentType) { this.entityProducer = new BasicAsyncEntityProducer(content, contentType); return this; } @Override public AsyncResponseProducer build() { final BasicHttpResponse response = new BasicHttpResponse(getStatus()); response.setVersion(getVersion()); response.setHeaders(getHeaders()); return new BasicResponseProducer(response, entityProducer); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("AsyncResponseBuilder [status="); builder.append(getStatus()); builder.append(", headerGroup="); builder.append(Arrays.toString(getHeaders())); builder.append(", entity="); builder.append(entityProducer != null ? entityProducer.getClass() : null); builder.append("]"); return builder.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java0100664 0000000 0000000 00000031126 14403631147 027665 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.support.AbstractRequestBuilder; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.net.WWWFormCodec; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * Builder for {@link AsyncRequestProducer} instances. *

* Please note that this class treats parameters differently depending on composition * of the request: if the request has a content entity explicitly set with * {@link #setEntity(AsyncEntityProducer)} or it is not an entity enclosing method * (such as POST or PUT), parameters will be added to the query component of the request URI. * Otherwise, parameters will be added as a URL encoded entity. *

* * @since 5.0 */ public class AsyncRequestBuilder extends AbstractRequestBuilder { private AsyncEntityProducer entityProducer; AsyncRequestBuilder(final String method) { super(method); } AsyncRequestBuilder(final Method method) { super(method); } AsyncRequestBuilder(final String method, final URI uri) { super(method, uri); } AsyncRequestBuilder(final Method method, final URI uri) { this(method.name(), uri); } AsyncRequestBuilder(final Method method, final String uri) { this(method.name(), uri != null ? URI.create(uri) : null); } AsyncRequestBuilder(final String method, final String uri) { this(method, uri != null ? URI.create(uri) : null); } public static AsyncRequestBuilder create(final String method) { Args.notBlank(method, "HTTP method"); return new AsyncRequestBuilder(method); } public static AsyncRequestBuilder get() { return new AsyncRequestBuilder(Method.GET); } public static AsyncRequestBuilder get(final URI uri) { return new AsyncRequestBuilder(Method.GET, uri); } public static AsyncRequestBuilder get(final String uri) { return new AsyncRequestBuilder(Method.GET, uri); } public static AsyncRequestBuilder head() { return new AsyncRequestBuilder(Method.HEAD); } public static AsyncRequestBuilder head(final URI uri) { return new AsyncRequestBuilder(Method.HEAD, uri); } public static AsyncRequestBuilder head(final String uri) { return new AsyncRequestBuilder(Method.HEAD, uri); } public static AsyncRequestBuilder patch() { return new AsyncRequestBuilder(Method.PATCH); } public static AsyncRequestBuilder patch(final URI uri) { return new AsyncRequestBuilder(Method.PATCH, uri); } public static AsyncRequestBuilder patch(final String uri) { return new AsyncRequestBuilder(Method.PATCH, uri); } public static AsyncRequestBuilder post() { return new AsyncRequestBuilder(Method.POST); } public static AsyncRequestBuilder post(final URI uri) { return new AsyncRequestBuilder(Method.POST, uri); } public static AsyncRequestBuilder post(final String uri) { return new AsyncRequestBuilder(Method.POST, uri); } public static AsyncRequestBuilder put() { return new AsyncRequestBuilder(Method.PUT); } public static AsyncRequestBuilder put(final URI uri) { return new AsyncRequestBuilder(Method.PUT, uri); } public static AsyncRequestBuilder put(final String uri) { return new AsyncRequestBuilder(Method.PUT, uri); } public static AsyncRequestBuilder delete() { return new AsyncRequestBuilder(Method.DELETE); } public static AsyncRequestBuilder delete(final URI uri) { return new AsyncRequestBuilder(Method.DELETE, uri); } public static AsyncRequestBuilder delete(final String uri) { return new AsyncRequestBuilder(Method.DELETE, uri); } public static AsyncRequestBuilder trace() { return new AsyncRequestBuilder(Method.TRACE); } public static AsyncRequestBuilder trace(final URI uri) { return new AsyncRequestBuilder(Method.TRACE, uri); } public static AsyncRequestBuilder trace(final String uri) { return new AsyncRequestBuilder(Method.TRACE, uri); } public static AsyncRequestBuilder options() { return new AsyncRequestBuilder(Method.OPTIONS); } public static AsyncRequestBuilder options(final URI uri) { return new AsyncRequestBuilder(Method.OPTIONS, uri); } public static AsyncRequestBuilder options(final String uri) { return new AsyncRequestBuilder(Method.OPTIONS, uri); } @Override public AsyncRequestBuilder setVersion(final ProtocolVersion version) { super.setVersion(version); return this; } @Override public AsyncRequestBuilder setUri(final URI uri) { super.setUri(uri); return this; } @Override public AsyncRequestBuilder setUri(final String uri) { super.setUri(uri); return this; } @Override public AsyncRequestBuilder setScheme(final String scheme) { super.setScheme(scheme); return this; } @Override public AsyncRequestBuilder setAuthority(final URIAuthority authority) { super.setAuthority(authority); return this; } /** * @since 5.1 */ @Override public AsyncRequestBuilder setHttpHost(final HttpHost httpHost) { super.setHttpHost(httpHost); return this; } @Override public AsyncRequestBuilder setPath(final String path) { super.setPath(path); return this; } @Override public AsyncRequestBuilder setHeaders(final Header... headers) { super.setHeaders(headers); return this; } @Override public AsyncRequestBuilder addHeader(final Header header) { super.addHeader(header); return this; } @Override public AsyncRequestBuilder addHeader(final String name, final String value) { super.addHeader(name, value); return this; } @Override public AsyncRequestBuilder removeHeader(final Header header) { super.removeHeader(header); return this; } @Override public AsyncRequestBuilder removeHeaders(final String name) { super.removeHeaders(name); return this; } @Override public AsyncRequestBuilder setHeader(final Header header) { super.setHeader(header); return this; } @Override public AsyncRequestBuilder setHeader(final String name, final String value) { super.setHeader(name, value); return this; } @Override public AsyncRequestBuilder setCharset(final Charset charset) { super.setCharset(charset); return this; } @Override public AsyncRequestBuilder addParameter(final NameValuePair nvp) { super.addParameter(nvp); return this; } @Override public AsyncRequestBuilder addParameter(final String name, final String value) { super.addParameter(name, value); return this; } @Override public AsyncRequestBuilder addParameters(final NameValuePair... nvps) { super.addParameters(nvps); return this; } @Override public AsyncRequestBuilder setAbsoluteRequestUri(final boolean absoluteRequestUri) { super.setAbsoluteRequestUri(absoluteRequestUri); return this; } public AsyncEntityProducer getEntity() { return entityProducer; } public AsyncRequestBuilder setEntity(final AsyncEntityProducer entityProducer) { this.entityProducer = entityProducer; return this; } public AsyncRequestBuilder setEntity(final String content, final ContentType contentType) { this.entityProducer = new BasicAsyncEntityProducer(content, contentType); return this; } public AsyncRequestBuilder setEntity(final String content) { this.entityProducer = new BasicAsyncEntityProducer(content); return this; } public AsyncRequestBuilder setEntity(final byte[] content, final ContentType contentType) { this.entityProducer = new BasicAsyncEntityProducer(content, contentType); return this; } @Override public AsyncRequestProducer build() { String path = getPath(); if (TextUtils.isEmpty(path)) { path = "/"; } AsyncEntityProducer entityProducerCopy = entityProducer; final String method = getMethod(); final List parameters = getParameters(); if (parameters != null && !parameters.isEmpty()) { final Charset charset = getCharset(); if (entityProducerCopy == null && (Method.POST.isSame(method) || Method.PUT.isSame(method))) { final String content = WWWFormCodec.format( parameters, charset != null ? charset : ContentType.APPLICATION_FORM_URLENCODED.getCharset()); entityProducerCopy = new StringAsyncEntityProducer( content, ContentType.APPLICATION_FORM_URLENCODED); } else { try { final URI uri = new URIBuilder(path) .setCharset(charset) .addParameters(parameters) .build(); path = uri.toASCIIString(); } catch (final URISyntaxException ex) { // should never happen } } } if (entityProducerCopy != null && Method.TRACE.isSame(method)) { throw new IllegalStateException(Method.TRACE + " requests may not include an entity"); } final BasicHttpRequest request = new BasicHttpRequest(method, getScheme(), getAuthority(), path); request.setVersion(getVersion()); request.setHeaders(getHeaders()); request.setAbsoluteRequestUri(isAbsoluteRequestUri()); return new BasicRequestProducer(request, entityProducerCopy); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("AsyncRequestBuilder [method="); builder.append(getMethod()); builder.append(", scheme="); builder.append(getScheme()); builder.append(", authority="); builder.append(getAuthority()); builder.append(", path="); builder.append(getPath()); builder.append(", parameters="); builder.append(getParameters()); builder.append(", headerGroup="); builder.append(Arrays.toString(getHeaders())); builder.append(", entity="); builder.append(entityProducer != null ? entityProducer.getClass() : null); builder.append("]"); return builder.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncServerExpectationFilter.java0100664 0000000 0000000 00000007225 14245617503 031555 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncFilterChain; import org.apache.hc.core5.http.nio.AsyncFilterHandler; import org.apache.hc.core5.http.protocol.HttpContext; /** * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class AsyncServerExpectationFilter implements AsyncFilterHandler { protected boolean verify(final HttpRequest request, final HttpContext context) throws HttpException { return true; } protected AsyncEntityProducer generateResponseContent(final HttpResponse expectationFailed) throws HttpException { return null; } @Override public final AsyncDataConsumer handle( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context, final AsyncFilterChain.ResponseTrigger responseTrigger, final AsyncFilterChain chain) throws HttpException, IOException { if (entityDetails != null) { final Header h = request.getFirstHeader(HttpHeaders.EXPECT); if (h != null && HeaderElements.CONTINUE.equalsIgnoreCase(h.getValue())) { final boolean verified = verify(request, context); if (verified) { responseTrigger.sendInformation(new BasicHttpResponse(HttpStatus.SC_CONTINUE)); } else { final HttpResponse expectationFailed = new BasicHttpResponse(HttpStatus.SC_EXPECTATION_FAILED); final AsyncEntityProducer responseContentProducer = generateResponseContent(expectationFailed); responseTrigger.submitResponse(expectationFailed, responseContentProducer); return null; } } } return chain.proceed(request, entityDetails, context, responseTrigger); } } ././@LongLink0100644 0000000 0000000 00000000147 14245617503 011644 Lustar 0000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.javahttpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.ja0100664 0000000 0000000 00000014460 14245617503 032664 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncResponseProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * {@link AsyncServerExchangeHandler} implementation that adds support * for the Expect-Continue handshake to an existing * {@link AsyncServerExchangeHandler}. * * @since 5.0 */ public class BasicAsyncServerExpectationDecorator implements AsyncServerExchangeHandler { private final AsyncServerExchangeHandler handler; private final Callback exceptionCallback; private final AtomicReference responseProducerRef; public BasicAsyncServerExpectationDecorator(final AsyncServerExchangeHandler handler, final Callback exceptionCallback) { this.handler = Args.notNull(handler, "Handler"); this.exceptionCallback = exceptionCallback; this.responseProducerRef = new AtomicReference<>(); } public BasicAsyncServerExpectationDecorator(final AsyncServerExchangeHandler handler) { this(handler, null); } protected AsyncResponseProducer verify( final HttpRequest request, final HttpContext context) throws IOException, HttpException { return null; } @Override public final void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { if (entityDetails != null) { final Header h = request.getFirstHeader(HttpHeaders.EXPECT); if (h != null && HeaderElements.CONTINUE.equalsIgnoreCase(h.getValue())) { final AsyncResponseProducer producer = verify(request, context); if (producer != null) { responseProducerRef.set(producer); producer.sendResponse(responseChannel, context); return; } responseChannel.sendInformation(new BasicHttpResponse(HttpStatus.SC_CONTINUE), context); } } handler.handleRequest(request, entityDetails, responseChannel, context); } @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { final AsyncResponseProducer responseProducer = responseProducerRef.get(); if (responseProducer == null) { handler.updateCapacity(capacityChannel); } else { capacityChannel.update(Integer.MAX_VALUE); } } @Override public final void consume(final ByteBuffer src) throws IOException { final AsyncResponseProducer responseProducer = responseProducerRef.get(); if (responseProducer == null) { handler.consume(src); } } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { final AsyncResponseProducer responseProducer = responseProducerRef.get(); if (responseProducer == null) { handler.streamEnd(trailers); } } @Override public final int available() { final AsyncResponseProducer responseProducer = responseProducerRef.get(); return responseProducer == null ? handler.available() : responseProducer.available(); } @Override public final void produce(final DataStreamChannel channel) throws IOException { final AsyncResponseProducer responseProducer = responseProducerRef.get(); if (responseProducer == null) { handler.produce(channel); } else { responseProducer.produce(channel); } } @Override public final void failed(final Exception cause) { if (exceptionCallback != null) { exceptionCallback.execute(cause); } final AsyncResponseProducer dataProducer = responseProducerRef.get(); if (dataProducer == null) { handler.failed(cause); } else { dataProducer.failed(cause); } } @Override public final void releaseResources() { handler.releaseResources(); final AsyncResponseProducer dataProducer = responseProducerRef.getAndSet(null); if (dataProducer != null) { dataProducer.releaseResources(); } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicResponseConsumer.java0100664 0000000 0000000 00000012516 14403631147 030206 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Basic implementation of {@link AsyncResponseConsumer} that represents response message as * a {@link Message} and relies on a {@link AsyncEntityConsumer} to process response entity * stream. * * @since 5.0 */ public class BasicResponseConsumer implements AsyncResponseConsumer> { private final Supplier> dataConsumerSupplier; private final AtomicReference> dataConsumerRef; public BasicResponseConsumer(final Supplier> dataConsumerSupplier) { this.dataConsumerSupplier = Args.notNull(dataConsumerSupplier, "Data consumer supplier"); this.dataConsumerRef = new AtomicReference<>(); } public BasicResponseConsumer(final AsyncEntityConsumer dataConsumer) { this(() -> dataConsumer); } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext httpContext, final FutureCallback> resultCallback) throws HttpException, IOException { Args.notNull(response, "Response"); if (entityDetails != null) { final AsyncEntityConsumer dataConsumer = dataConsumerSupplier.get(); if (dataConsumer == null) { throw new HttpException("Supplied data consumer is null"); } dataConsumerRef.set(dataConsumer); dataConsumer.streamStart(entityDetails, new CallbackContribution(resultCallback) { @Override public void completed(final T body) { final Message result = new Message<>(response, body); if (resultCallback != null) { resultCallback.completed(result); } } }); } else { final Message result = new Message<>(response, null); if (resultCallback != null) { resultCallback.completed(result); } } } @Override public void informationResponse(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); dataConsumer.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); dataConsumer.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); dataConsumer.streamEnd(trailers); } @Override public void failed(final Exception cause) { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); if (dataConsumer != null) { dataConsumer.failed(cause); } releaseResources(); } @Override public void releaseResources() { final AsyncEntityConsumer dataConsumer = dataConsumerRef.getAndSet(null); if (dataConsumer != null) { dataConsumer.releaseResources(); } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractAsyncServerAuthFilter.java0100664 0000000 0000000 00000016570 14245617503 031662 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncFilterChain; import org.apache.hc.core5.http.nio.AsyncFilterHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIAuthority; /** * Abstract asynchronous HTTP request filter that implements standard HTTP authentication handshake. * * @param authorization token representation. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public abstract class AbstractAsyncServerAuthFilter implements AsyncFilterHandler { private final boolean respondImmediately; protected AbstractAsyncServerAuthFilter(final boolean respondImmediately) { this.respondImmediately = respondImmediately; } /** * Parses authorization header value into an authentication token sent by the client * as a response to an authentication challenge. * * @param authorizationValue the authorization header value. * @param context the actual execution context. * @return authorization token */ protected abstract T parseChallengeResponse(String authorizationValue, HttpContext context) throws HttpException; /** * Authenticates the client using the authentication token sent by the client * as a response to an authentication challenge. * * @param challengeResponse the authentication token sent by the client * as a response to an authentication challenge. * @param authority the URI authority. * @param requestUri the request URI. * @param context the actual execution context. * @return {@code true} if the client could be successfully authenticated {@code false} otherwise. */ protected abstract boolean authenticate(T challengeResponse, URIAuthority authority, String requestUri, HttpContext context); /** * Generates an authentication challenge in case of unsuccessful authentication. * * @param challengeResponse the authentication token sent by the client * as a response to an authentication challenge * or {@code null} if the client has not sent any. * @param authority the URI authority. * @param requestUri the request URI. * @param context the actual execution context. * @return an authorization challenge value. */ protected abstract String generateChallenge(T challengeResponse, URIAuthority authority, String requestUri, HttpContext context); /** * Generates response body for UNAUTHORIZED response. * * @param unauthorized the response to return as a result of authentication failure. * @return the response content entity. */ protected AsyncEntityProducer generateResponseContent(final HttpResponse unauthorized) { return AsyncEntityProducers.create("Unauthorized"); } @Override public final AsyncDataConsumer handle( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context, final AsyncFilterChain.ResponseTrigger responseTrigger, final AsyncFilterChain chain) throws HttpException, IOException { final Header h = request.getFirstHeader(HttpHeaders.AUTHORIZATION); final T challengeResponse = h != null ? parseChallengeResponse(h.getValue(), context) : null; final URIAuthority authority = request.getAuthority(); final String requestUri = request.getRequestUri(); final boolean authenticated = authenticate(challengeResponse, authority, requestUri, context); final Header expect = request.getFirstHeader(HttpHeaders.EXPECT); final boolean expectContinue = expect != null && HeaderElements.CONTINUE.equalsIgnoreCase(expect.getValue()); if (authenticated) { if (expectContinue) { responseTrigger.sendInformation(new BasicClassicHttpResponse(HttpStatus.SC_CONTINUE)); } return chain.proceed(request, entityDetails, context, responseTrigger); } final HttpResponse unauthorized = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED); unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, generateChallenge(challengeResponse, authority, requestUri, context)); final AsyncEntityProducer responseContentProducer = generateResponseContent(unauthorized); if (respondImmediately || expectContinue || entityDetails == null) { responseTrigger.submitResponse(unauthorized, responseContentProducer); return null; } return new AsyncDataConsumer() { @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(Integer.MAX_VALUE); } @Override public void consume(final ByteBuffer src) throws IOException { } @Override public void streamEnd(final List trailers) throws HttpException, IOException { responseTrigger.submitResponse(unauthorized, responseContentProducer); } @Override public void releaseResources() { if (responseContentProducer != null) { responseContentProducer.releaseResources(); } } }; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractAsyncRequesterConsumer.java0100664 0000000 0000000 00000013120 14403631147 032077 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.UnsupportedCharsetException; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Abstract asynchronous request consumer that makes use of {@link AsyncEntityConsumer} * to process request message content. * * @param request processing result representation. * @param request entity representation. * * @since 5.0 */ public abstract class AbstractAsyncRequesterConsumer implements AsyncRequestConsumer { private final Supplier> dataConsumerSupplier; private final AtomicReference> dataConsumerRef; public AbstractAsyncRequesterConsumer(final Supplier> dataConsumerSupplier) { this.dataConsumerSupplier = Args.notNull(dataConsumerSupplier, "Data consumer supplier"); this.dataConsumerRef = new AtomicReference<>(); } public AbstractAsyncRequesterConsumer(final AsyncEntityConsumer dataConsumer) { this(() -> dataConsumer); } /** * Triggered to generate object that represents a result of request message processing. * @param request the request message. * @param entity the request entity. * @param contentType the request content type. * @return the result of request processing. */ protected abstract T buildResult(HttpRequest request, E entity, ContentType contentType); @Override public final void consumeRequest( final HttpRequest request, final EntityDetails entityDetails, final HttpContext httpContext, final FutureCallback resultCallback) throws HttpException, IOException { if (entityDetails != null) { final AsyncEntityConsumer dataConsumer = dataConsumerSupplier.get(); if (dataConsumer == null) { throw new HttpException("Supplied data consumer is null"); } dataConsumerRef.set(dataConsumer); dataConsumer.streamStart(entityDetails, new CallbackContribution(resultCallback) { @Override public void completed(final E entity) { final ContentType contentType; try { contentType = ContentType.parse(entityDetails.getContentType()); final T result = buildResult(request, entity, contentType); resultCallback.completed(result); } catch (final UnsupportedCharsetException ex) { resultCallback.failed(ex); } } }); } else { resultCallback.completed(buildResult(request, null, null)); } } @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); dataConsumer.updateCapacity(capacityChannel); } @Override public final void consume(final ByteBuffer src) throws IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); dataConsumer.consume(src); } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { final AsyncEntityConsumer dataConsumer = dataConsumerRef.get(); dataConsumer.streamEnd(trailers); } @Override public final void failed(final Exception cause) { releaseResources(); } @Override public final void releaseResources() { final AsyncEntityConsumer dataConsumer = dataConsumerRef.getAndSet(null); if (dataConsumer != null) { dataConsumer.releaseResources(); } } }httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java0100664 0000000 0000000 00000021706 14245617503 031632 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncResponseProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Asserts; /** * Abstract server side message exchange handler. * * @since 5.0 */ public abstract class AbstractServerExchangeHandler implements AsyncServerExchangeHandler { private final AtomicReference> requestConsumerRef; private final AtomicReference responseProducerRef; public AbstractServerExchangeHandler() { this.requestConsumerRef = new AtomicReference<>(); this.responseProducerRef = new AtomicReference<>(); } /** * Triggered to supply a request consumer to process the incoming request. * @param request the request message. * @param entityDetails the request entity details. * @param context the actual execution context. * @return the request consumer. */ protected abstract AsyncRequestConsumer supplyConsumer( HttpRequest request, EntityDetails entityDetails, HttpContext context) throws HttpException; /** * Triggered to handles the request object produced by the {@link AsyncRequestConsumer} returned * from the {@link #supplyConsumer(HttpRequest, EntityDetails, HttpContext)} method. The handler * can choose to send response messages immediately inside the call or asynchronously * at some later point. * * @param requestMessage the request message. * @param responseTrigger the response trigger. * @param context the actual execution context. */ protected abstract void handle( T requestMessage, AsyncServerRequestHandler.ResponseTrigger responseTrigger, HttpContext context) throws HttpException, IOException; @Override public final void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final AsyncRequestConsumer requestConsumer = supplyConsumer(request, entityDetails, context); if (requestConsumer == null) { throw new HttpException("Unable to handle request"); } requestConsumerRef.set(requestConsumer); final AsyncServerRequestHandler.ResponseTrigger responseTrigger = new AsyncServerRequestHandler.ResponseTrigger() { @Override public void sendInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { responseChannel.sendInformation(response, httpContext); } @Override public void submitResponse( final AsyncResponseProducer producer, final HttpContext httpContext) throws HttpException, IOException { if (responseProducerRef.compareAndSet(null, producer)) { producer.sendResponse(responseChannel, httpContext); } } @Override public void pushPromise( final HttpRequest promise, final HttpContext httpContext, final AsyncPushProducer pushProducer) throws HttpException, IOException { responseChannel.pushPromise(promise, pushProducer, httpContext); } @Override public String toString() { return "Response trigger: " + responseChannel; } }; requestConsumer.consumeRequest(request, entityDetails, context, new FutureCallback() { @Override public void completed(final T result) { try { handle(result, responseTrigger, context); } catch (final HttpException ex) { try { responseTrigger.submitResponse( AsyncResponseBuilder.create(HttpStatus.SC_INTERNAL_SERVER_ERROR) .setEntity(ex.getMessage()) .build(), context); } catch (final HttpException | IOException ex2) { failed(ex2); } } catch (final IOException ex) { failed(ex); } } @Override public void failed(final Exception ex) { AbstractServerExchangeHandler.this.failed(ex); } @Override public void cancelled() { releaseResources(); } }); } @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { final AsyncRequestConsumer requestConsumer = requestConsumerRef.get(); Asserts.notNull(requestConsumer, "Data consumer"); requestConsumer.updateCapacity(capacityChannel); } @Override public final void consume(final ByteBuffer src) throws IOException { final AsyncRequestConsumer requestConsumer = requestConsumerRef.get(); Asserts.notNull(requestConsumer, "Data consumer"); requestConsumer.consume(src); } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { final AsyncRequestConsumer requestConsumer = requestConsumerRef.get(); Asserts.notNull(requestConsumer, "Data consumer"); requestConsumer.streamEnd(trailers); } @Override public final int available() { final AsyncResponseProducer dataProducer = responseProducerRef.get(); return dataProducer != null ? dataProducer.available() : 0; } @Override public final void produce(final DataStreamChannel channel) throws IOException { final AsyncResponseProducer dataProducer = responseProducerRef.get(); Asserts.notNull(dataProducer, "Data producer"); dataProducer.produce(channel); } @Override public final void failed(final Exception cause) { try { final AsyncRequestConsumer requestConsumer = requestConsumerRef.get(); if (requestConsumer != null) { requestConsumer.failed(cause); } final AsyncResponseProducer dataProducer = responseProducerRef.get(); if (dataProducer != null) { dataProducer.failed(cause); } } finally { releaseResources(); } } @Override public final void releaseResources() { final AsyncRequestConsumer requestConsumer = requestConsumerRef.getAndSet(null); if (requestConsumer != null) { requestConsumer.releaseResources(); } final AsyncResponseProducer dataProducer = responseProducerRef.getAndSet(null); if (dataProducer != null) { dataProducer.releaseResources(); } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicPushProducer.java0100664 0000000 0000000 00000006521 14245617503 027322 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Basic implementation of {@link AsyncPushProducer} that produces one fixed response * and relies on a {@link AsyncEntityProducer} to generate response entity stream. * * @since 5.0 */ public class BasicPushProducer implements AsyncPushProducer { private final HttpResponse response; private final AsyncEntityProducer dataProducer; public BasicPushProducer(final HttpResponse response, final AsyncEntityProducer dataProducer) { this.response = Args.notNull(response, "Response"); this.dataProducer = Args.notNull(dataProducer, "Entity producer"); } public BasicPushProducer(final int code, final AsyncEntityProducer dataProducer) { this(new BasicHttpResponse(code), dataProducer); } public BasicPushProducer(final AsyncEntityProducer dataProducer) { this(HttpStatus.SC_OK, dataProducer); } @Override public void produceResponse(final ResponseChannel channel, final HttpContext httpContext) throws HttpException, IOException { channel.sendResponse(response, dataProducer, httpContext); } @Override public int available() { return dataProducer != null ? dataProducer.available() : 0; } @Override public void produce(final DataStreamChannel channel) throws IOException { if (dataProducer != null) { dataProducer.produce(channel); } } @Override public void failed(final Exception cause) { releaseResources(); } @Override public void releaseResources() { if (dataProducer != null) { dataProducer.releaseResources(); } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/TerminalAsyncServerFilter.java0100664 0000000 0000000 00000014614 14245617503 031045 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support; import java.io.IOException; import java.util.Set; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncFilterChain; import org.apache.hc.core5.http.nio.AsyncFilterHandler; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * {@link AsyncFilterHandler} implementation represents a terminal handler * in an asynchronous request processing pipeline that makes use of {@link HandlerFactory} * to dispatch the request to a particular {@link AsyncServerExchangeHandler}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public final class TerminalAsyncServerFilter implements AsyncFilterHandler { final private HandlerFactory handlerFactory; public TerminalAsyncServerFilter(final HandlerFactory handlerFactory) { this.handlerFactory = Args.notNull(handlerFactory, "Handler factory"); } @Override public AsyncDataConsumer handle( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context, final AsyncFilterChain.ResponseTrigger responseTrigger, final AsyncFilterChain chain) throws HttpException, IOException { final AsyncServerExchangeHandler exchangeHandler = handlerFactory.create(request, context); if (exchangeHandler != null) { exchangeHandler.handleRequest(request, entityDetails, new ResponseChannel() { @Override public void sendInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { responseTrigger.sendInformation(response); } @Override public void sendResponse(final HttpResponse response, final EntityDetails entityDetails, final HttpContext httpContext) throws HttpException, IOException { responseTrigger.submitResponse(response, entityDetails != null ? new AsyncEntityProducer() { @Override public void failed(final Exception cause) { exchangeHandler.failed(cause); } @Override public boolean isRepeatable() { return false; } @Override public long getContentLength() { return entityDetails.getContentLength(); } @Override public String getContentType() { return entityDetails.getContentType(); } @Override public String getContentEncoding() { return entityDetails.getContentEncoding(); } @Override public boolean isChunked() { return entityDetails.isChunked(); } @Override public Set getTrailerNames() { return entityDetails.getTrailerNames(); } @Override public int available() { return exchangeHandler.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { exchangeHandler.produce(channel); } @Override public void releaseResources() { exchangeHandler.releaseResources(); } } : null); } @Override public void pushPromise(final HttpRequest promise, final AsyncPushProducer pushProducer, final HttpContext httpContext) throws HttpException, IOException { responseTrigger.pushPromise(promise, pushProducer); } }, context); return exchangeHandler; } responseTrigger.submitResponse(new BasicHttpResponse(HttpStatus.SC_NOT_FOUND), AsyncEntityProducers.create("Not found")); return null; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentDecoder.java0100664 0000000 0000000 00000004521 14245617503 025117 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.http.Header; /** * Abstract HTTP content decoder. HTTP content decoders can be used * to read entity content from the underlying channel in small * chunks and apply the required coding transformation. * * @since 4.0 */ public interface ContentDecoder { /** * Reads a portion of content from the underlying channel * * @param dst The buffer into which entity content is to be transferred * @return The number of bytes read, possibly zero, or -1 if the * channel has reached end-of-stream * @throws IOException if I/O error occurs while reading content */ int read(ByteBuffer dst) throws IOException; /** * Returns {@code true} if the entity has been received in its * entirety. * * @return {@code true} if all the content has been consumed, * {@code false} otherwise. */ boolean isCompleted(); /** * Returns content trailers if available * * @return list of trailers * * @since 5.0 */ List getTrailers(); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncClientEndpoint.java0100664 0000000 0000000 00000012653 14245617503 026141 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.util.concurrent.Future; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.concurrent.FutureContribution; import org.apache.hc.core5.http.nio.support.BasicClientExchangeHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; /** * Client endpoint leased from a connection manager. *

* Once the endpoint is no longer needed it MUST be released with {@link #releaseAndReuse()} * or {@link #releaseAndDiscard()}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public abstract class AsyncClientEndpoint { /** * Initiates a message exchange using the given handler. *

* Once the endpoint is no longer needed it MUST be released with {@link #releaseAndReuse()} * or {@link #releaseAndDiscard()}. */ public abstract void execute( AsyncClientExchangeHandler exchangeHandler, HandlerFactory pushHandlerFactory, HttpContext context); /** * Initiates a message exchange using the given handler. *

* Once the endpoint is no longer needed it MUST be released with {@link #releaseAndReuse()} * or {@link #releaseAndDiscard()}. */ public void execute( final AsyncClientExchangeHandler exchangeHandler, final HttpContext context) { execute(exchangeHandler, null, context); } /** * Releases the underlying connection back to the connection pool as re-usable. */ public abstract void releaseAndReuse(); /** * Shuts down the underlying connection and removes it from the connection pool. */ public abstract void releaseAndDiscard(); /** * Determines if the connection to the remote endpoint is still open and valid. */ public abstract boolean isConnected(); /** * Initiates message exchange using the given request producer and response consumer. *

* Once the endpoint is no longer needed it MUST be released with {@link #releaseAndReuse()} * or {@link #releaseAndDiscard()}. */ public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HandlerFactory pushHandlerFactory, final HttpContext context, final FutureCallback callback) { final BasicFuture future = new BasicFuture<>(callback); execute(new BasicClientExchangeHandler<>(requestProducer, responseConsumer, new FutureContribution(future) { @Override public void completed(final T result) { future.completed(result); } }), pushHandlerFactory, context != null ? context : HttpCoreContext.create()); return future; } /** * Initiates message exchange using the given request producer and response consumer. *

* Once the endpoint is no longer needed it MUST be released with {@link #releaseAndReuse()} * or {@link #releaseAndDiscard()}. */ public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HttpContext context, final FutureCallback callback) { return execute(requestProducer, responseConsumer, null, context, callback); } /** * Initiates a message exchange using the given request producer and response consumer. *

* Once the endpoint is no longer needed it MUST be released with {@link #releaseAndReuse()} * or {@link #releaseAndDiscard()}. */ public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final FutureCallback callback) { return execute(requestProducer, responseConsumer, null, null, callback); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/FileContentEncoder.java0100664 0000000 0000000 00000004162 14245617503 025732 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import java.nio.channels.FileChannel; /** * A content encoder capable of transferring data directly from a {@link FileChannel} * * @since 4.0 */ public interface FileContentEncoder extends ContentEncoder { /** * Transfers a portion of entity content from the given file channel * to the underlying network channel. * * @param src the source FileChannel to transfer data from. * @param position * The position within the file at which the transfer is to begin; * must be non-negative * @param count * The maximum number of bytes to be transferred; must be * non-negative * @throws IOException if some I/O error occurs. * @return The number of bytes, possibly zero, * that were actually transferred */ long transfer(FileChannel src, long position, long count) throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataProducer.java0100664 0000000 0000000 00000003770 14245617503 025577 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; /** * Abstract asynchronous data producer. * * @since 5.0 */ public interface AsyncDataProducer extends ResourceHolder { /** * Returns the number of bytes immediately available for output. * This method can be used as a hint to control output events * of the underlying I/O session. * * @return the number of bytes immediately available for output */ int available(); /** * Triggered to signal the ability of the underlying data channel * to accept more data. The data producer can choose to write data * immediately inside the call or asynchronously at some later point. * * @param channel the data channel capable to accepting more data. */ void produce(DataStreamChannel channel) throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/HandlerFactory.java0100664 0000000 0000000 00000003460 14403631147 025121 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract handler factory. * * @param handler type * * @since 5.0 */ public interface HandlerFactory { /** * Creates a new handler instance based on properties of * an incoming request message.. * * @param request the incoming request head. * @param context the actual execution context. * @return handler */ T create(HttpRequest request, HttpContext context) throws HttpException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/SessionInputBuffer.java0100664 0000000 0000000 00000013571 14245617503 026021 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import org.apache.hc.core5.util.CharArrayBuffer; /** * Session input buffer for HTTP/1.1 non-blocking connections. *

* This interface facilitates intermediate buffering of input data streamed from * a source channel and provides methods for reading lines of text. *

* * @since 4.0 */ public interface SessionInputBuffer { /** * Determines if the buffer contains data. * * @return {@code true} if there is data in the buffer, * {@code false} otherwise. */ boolean hasData(); /** * Returns the length of this buffer. * * @return buffer length. */ int length(); /** * Makes an attempt to fill the buffer with data from the given * {@link ReadableByteChannel}. * * @param src the source channel * @return The number of bytes read, possibly zero, or {@code -1} if the * channel has reached end-of-stream. * @throws IOException in case of an I/O error. */ int fill(ReadableByteChannel src) throws IOException; /** * Reads one byte from the buffer. If the buffer is empty this method can * throw a runtime exception. The exact type of runtime exception thrown * by this method depends on implementation. * * @return one byte */ int read(); /** * Reads a sequence of bytes from this buffer into the destination buffer, * up to the given maximum limit. The exact number of bytes transferred * depends on availability of data in this buffer and capacity of the * destination buffer, but cannot be more than {@code maxLen} value. * * @param dst the destination buffer. * @param maxLen the maximum number of bytes to be read. * @return The number of bytes read, possibly zero. */ int read(ByteBuffer dst, int maxLen); /** * Reads a sequence of bytes from this buffer into the destination buffer. * The exact number of bytes transferred depends on availability of data * in this buffer and capacity of the destination buffer. * * @param dst the destination buffer. * @return The number of bytes read, possibly zero. */ int read(ByteBuffer dst); /** * Reads a sequence of bytes from this buffer into the destination channel, * up to the given maximum limit. The exact number of bytes transferred * depends on availability of data in this buffer, but cannot be more than * {@code maxLen} value. * * @param dst the destination channel. * @param maxLen the maximum number of bytes to be read. * @return The number of bytes read, possibly zero. * @throws IOException in case of an I/O error. */ int read(WritableByteChannel dst, int maxLen) throws IOException; /** * Reads a sequence of bytes from this buffer into the destination channel. * The exact number of bytes transferred depends on availability of data in * this buffer. * * @param dst the destination channel. * @return The number of bytes read, possibly zero. * @throws IOException in case of an I/O error. */ int read(WritableByteChannel dst) throws IOException; /** * Attempts to transfer a complete line of characters up to a line delimiter * from this buffer to the destination buffer. If a complete line is * available in the buffer, the sequence of chars is transferred to the * destination buffer the method returns {@code true}. The line * delimiter itself is discarded. If a complete line is not available in * the buffer, this method returns {@code false} without transferring * anything to the destination buffer. If {@code endOfStream} parameter * is set to {@code true} this method assumes the end of stream has * been reached and the content currently stored in the buffer should be * treated as a complete line. *

* The choice of a char encoding and line delimiter sequence is up to the * specific implementations of this interface. * * @param dst the destination buffer. * @param endOfStream end of stream flag * @return {@code true} if a sequence of chars representing a complete * line has been transferred to the destination buffer, {@code false} * otherwise. * * @throws java.nio.charset.CharacterCodingException in case a character encoding or decoding * error occurs. * @throws org.apache.hc.core5.http.MessageConstraintException in case a message constraint violation. */ boolean readLine(CharArrayBuffer dst, boolean endOfStream) throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/command/0040775 0000000 0000000 00000000000 14403631147 022767 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/command/package-info.java0100664 0000000 0000000 00000002446 14245617503 026165 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Commands for HTTP transports based on asynchronous, event driven I/O model. */ package org.apache.hc.core5.http.nio.command; httpcore5/src/main/java/org/apache/hc/core5/http/nio/command/ShutdownCommand.java0100664 0000000 0000000 00000005077 14403631147 026752 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.command; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.IOSession; /** * Shutdown command. Two shutdown modes are supported: {@link CloseMode#GRACEFUL} and * {@link CloseMode#IMMEDIATE}. The exact implementation of both modes is protocol or handler * specific. * * @since 5.0 */ public final class ShutdownCommand implements Command { public static final ShutdownCommand GRACEFUL = new ShutdownCommand(CloseMode.GRACEFUL); public static final ShutdownCommand IMMEDIATE = new ShutdownCommand(CloseMode.IMMEDIATE); public static final Callback GRACEFUL_IMMEDIATE_CALLBACK = createIOSessionCallback(Priority.IMMEDIATE); public static final Callback GRACEFUL_NORMAL_CALLBACK = createIOSessionCallback(Priority.NORMAL); private static Callback createIOSessionCallback(final Priority priority) { return session -> session.enqueue(ShutdownCommand.GRACEFUL, priority); } private final CloseMode type; public ShutdownCommand(final CloseMode type) { this.type = type; } public CloseMode getType() { return type; } @Override public boolean cancel() { return true; } @Override public String toString() { return "Shutdown: " + type; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/command/CommandSupport.java0100664 0000000 0000000 00000005173 14323605260 026606 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.command; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.RequestNotExecutedException; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Args; /** * {@link Command} utility methods. * * @since 5.0 */ @Internal public final class CommandSupport { /** * Fails all pending session {@link Command}s. */ static public void failCommands(final IOSession ioSession, final Exception ex) { Args.notNull(ioSession, "I/O session"); Command command; while ((command = ioSession.poll()) != null) { if (command instanceof ExecutableCommand) { ((ExecutableCommand) command).failed(ex); } else { command.cancel(); } } } /** * Cancels all pending session {@link Command}s. */ static public void cancelCommands(final IOSession ioSession) { Args.notNull(ioSession, "I/O session"); Command command; while ((command = ioSession.poll()) != null) { if (command instanceof ExecutableCommand) { if (!ioSession.isOpen()) { ((ExecutableCommand) command).failed(new RequestNotExecutedException()); } else { command.cancel(); } } else { command.cancel(); } } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/command/RequestExecutionCommand.java0100664 0000000 0000000 00000010126 14323605260 030440 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.command; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.RequestNotExecutedException; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Request execution command. * * @since 5.0 */ @Internal public final class RequestExecutionCommand extends ExecutableCommand { private final AsyncClientExchangeHandler exchangeHandler; private final HandlerFactory pushHandlerFactory; private final CancellableDependency cancellableDependency; private final HttpContext context; private final AtomicBoolean failed; public RequestExecutionCommand( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final CancellableDependency cancellableDependency, final HttpContext context) { this.exchangeHandler = Args.notNull(exchangeHandler, "Handler"); this.pushHandlerFactory = pushHandlerFactory; this.cancellableDependency = cancellableDependency; this.context = context; this.failed = new AtomicBoolean(); } public RequestExecutionCommand( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final HttpContext context) { this(exchangeHandler, pushHandlerFactory, null, context); } public RequestExecutionCommand( final AsyncClientExchangeHandler exchangeHandler, final HttpContext context) { this(exchangeHandler, null, null, context); } public AsyncClientExchangeHandler getExchangeHandler() { return exchangeHandler; } public HandlerFactory getPushHandlerFactory() { return pushHandlerFactory; } @Override public CancellableDependency getCancellableDependency() { return cancellableDependency; } public HttpContext getContext() { return context; } @Override public void failed(final Exception ex) { if (failed.compareAndSet(false, true)) { try { exchangeHandler.failed(ex); } finally { exchangeHandler.releaseResources(); } } } @Override public boolean cancel() { if (failed.compareAndSet(false, true)) { try { exchangeHandler.failed(new RequestNotExecutedException()); return true; } finally { exchangeHandler.releaseResources(); } } return false; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/command/ExecutableCommand.java0100664 0000000 0000000 00000003427 14245617503 027221 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.command; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.reactor.Command; /** * Abstract executable command that may need to do some cleaning up * in case of an failure and also optionally may want to cancel * the associated HTTP message exchange through {@link CancellableDependency}. * * @since 5.0 */ @Internal public abstract class ExecutableCommand implements Command { public abstract CancellableDependency getCancellableDependency(); public abstract void failed(Exception ex); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/StreamChannel.java0100664 0000000 0000000 00000004002 14245617503 024735 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import java.nio.Buffer; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Abstract data stream channel. *

* Implementations are expected to be thread-safe. *

* * @param data container accepted by the channel. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public interface StreamChannel { /** * Writes data from the data container into the underlying data stream. * * @param src source of data * @return The number of elements written, possibly zero */ int write(T src) throws IOException; /** * Terminates the underlying data stream and optionally writes * a closing sequence. */ void endStream() throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncEntityConsumer.java0100664 0000000 0000000 00000004376 14245617503 026215 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; /** * Abstract asynchronous message entity consumer. * * @param entity representation. * * @since 5.0 */ public interface AsyncEntityConsumer extends AsyncDataConsumer { /** * Signals beginning of an incoming request entity stream. * * @param entityDetails the details of the incoming message entity. * @param resultCallback the result callback. */ void streamStart(EntityDetails entityDetails, FutureCallback resultCallback) throws HttpException, IOException; /** * Triggered to signal a failure in data processing. * * @param cause the cause of the failure. */ void failed(Exception cause); /** * Returns the result of entity processing when it becomes available or {@code null} * if the entity is still being received. * * @return the response processing result. */ T getContent(); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/0040775 0000000 0000000 00000000000 14403631147 022152 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/package-info.java0100664 0000000 0000000 00000002354 14245617503 025346 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * TLS protocol support. */ package org.apache.hc.core5.http.nio.ssl; httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/BasicClientTlsStrategy.java0100664 0000000 0000000 00000011345 14403631147 027404 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.ssl; import java.net.SocketAddress; import javax.net.ssl.SSLContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * Basic client-side implementation of {@link TlsStrategy} that upgrades to TLS for all endpoints * with {@code HTTPS} scheme. * * @since 5.0 */ public class BasicClientTlsStrategy implements TlsStrategy { private final SSLContext sslContext; private final SSLBufferMode sslBufferMode; private final SSLSessionInitializer initializer; private final SSLSessionVerifier verifier; public BasicClientTlsStrategy( final SSLContext sslContext, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this.sslContext = Args.notNull(sslContext, "SSL context"); this.sslBufferMode = sslBufferMode; this.initializer = initializer; this.verifier = verifier; } public BasicClientTlsStrategy( final SSLContext sslContext, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this(sslContext, null, initializer, verifier); } public BasicClientTlsStrategy( final SSLContext sslContext, final SSLSessionVerifier verifier) { this(sslContext, null, null, verifier); } public BasicClientTlsStrategy(final SSLContext sslContext) { this(sslContext, null, null, null); } public BasicClientTlsStrategy() { this(SSLContexts.createSystemDefault()); } /** * Constructor with the default SSL context based on system properties and custom {@link SSLSessionVerifier} verifier. * @param verifier the custom {@link SSLSessionVerifier}. * @see SSLContext * @since 5.2 */ public BasicClientTlsStrategy(final SSLSessionVerifier verifier) { this(SSLContexts.createSystemDefault(), verifier); } @Override public void upgrade( final TransportSecurityLayer tlsSession, final NamedEndpoint endpoint, final Object attachment, final Timeout handshakeTimeout, final FutureCallback callback) { tlsSession.startTls(sslContext, endpoint, sslBufferMode, TlsSupport.enforceStrongSecurity(initializer), verifier, handshakeTimeout, callback); } /** * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)} */ @Deprecated @Override public boolean upgrade( final TransportSecurityLayer tlsSession, final HttpHost host, final SocketAddress localAddress, final SocketAddress remoteAddress, final Object attachment, final Timeout handshakeTimeout) { final String scheme = host != null ? host.getSchemeName() : null; if (URIScheme.HTTPS.same(scheme)) { upgrade(tlsSession, host, attachment, handshakeTimeout, null); return true; } return false; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/FixedPortStrategy.java0100664 0000000 0000000 00000004070 14315123013 026430 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.ssl; import java.net.InetSocketAddress; import java.net.SocketAddress; import org.apache.hc.core5.util.Args; /** * Basic implementation of {@link SecurePortStrategy} with a fixed list of secure ports. * * @since 5.0 * * @deprecated Use configuration parameters provided by connection listeners. */ @Deprecated public final class FixedPortStrategy implements SecurePortStrategy { private final int[] securePorts; public FixedPortStrategy(final int... securePorts) { this.securePorts = Args.notNull(securePorts, "Secure ports"); } @Override public boolean isSecure(final SocketAddress localAddress) { final int port = ((InetSocketAddress) localAddress).getPort(); for (final int securePort: securePorts) { if (port == securePort) { return true; } } return false; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/TlsUpgradeCapable.java0100664 0000000 0000000 00000003143 14403631147 026335 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.ssl; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ProtocolIOSession; /** * Capability to upgrade to TLS. * * @since 5.2 */ @Internal public interface TlsUpgradeCapable { void tlsUpgrade(NamedEndpoint endpoint, final FutureCallback callback); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/SecurePortStrategy.java0100664 0000000 0000000 00000003277 14245617503 026645 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.ssl; import java.net.SocketAddress; /** * Side-side strategy to determine if local endpoint should be secured with TLS. * * @since 5.0 * * @deprecated Use configuration parameters provided by connection listeners. */ @Deprecated public interface SecurePortStrategy { /** * Determines if the given address should be secured or considered secure. * @param localAddress the given address. * * @return secure flag. */ boolean isSecure(SocketAddress localAddress); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/BasicServerTlsStrategy.java0100664 0000000 0000000 00000016022 14403631147 027431 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.ssl; import java.net.SocketAddress; import javax.net.ssl.SSLContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * Basic side-side implementation of {@link TlsStrategy} that upgrades to TLS for endpoints * with the specified local ports. * * @since 5.0 */ public class BasicServerTlsStrategy implements TlsStrategy { private final SSLContext sslContext; @SuppressWarnings("deprecation") private final SecurePortStrategy securePortStrategy; private final SSLBufferMode sslBufferMode; private final SSLSessionInitializer initializer; private final SSLSessionVerifier verifier; /** * @deprecated Use {@link BasicServerTlsStrategy#BasicServerTlsStrategy(SSLContext, SSLBufferMode, SSLSessionInitializer, SSLSessionVerifier)} */ @Deprecated public BasicServerTlsStrategy( final SSLContext sslContext, final SecurePortStrategy securePortStrategy, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this.sslContext = Args.notNull(sslContext, "SSL context"); this.securePortStrategy = securePortStrategy; this.sslBufferMode = sslBufferMode; this.initializer = initializer; this.verifier = verifier; } /** * @deprecated Use {@link BasicServerTlsStrategy#BasicServerTlsStrategy(SSLContext, SSLSessionInitializer, SSLSessionVerifier)} */ @Deprecated public BasicServerTlsStrategy( final SSLContext sslContext, final SecurePortStrategy securePortStrategy, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this(sslContext, securePortStrategy, null, initializer, verifier); } /** * @deprecated Use {@link BasicServerTlsStrategy#BasicServerTlsStrategy(SSLContext, SSLSessionVerifier)} */ @Deprecated public BasicServerTlsStrategy( final SSLContext sslContext, final SecurePortStrategy securePortStrategy, final SSLSessionVerifier verifier) { this(sslContext, securePortStrategy, null, null, verifier); } /** * @deprecated Use {@link BasicServerTlsStrategy#BasicServerTlsStrategy(SSLContext)} */ @Deprecated public BasicServerTlsStrategy(final SSLContext sslContext, final SecurePortStrategy securePortStrategy) { this(sslContext, securePortStrategy, null, null, null); } /** * @deprecated Use {@link BasicServerTlsStrategy#BasicServerTlsStrategy()} */ @Deprecated public BasicServerTlsStrategy(final SecurePortStrategy securePortStrategy) { this(SSLContexts.createSystemDefault(), securePortStrategy); } /** * Constructor with the default SSL context based on system properties and custom {@link SSLSessionVerifier} verifier. * @param verifier the custom {@link SSLSessionVerifier}. * @since 5.2 */ public BasicServerTlsStrategy(final SSLSessionVerifier verifier) { this(SSLContexts.createSystemDefault(), verifier); } public BasicServerTlsStrategy( final SSLContext sslContext, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this.sslContext = Args.notNull(sslContext, "SSL context"); this.sslBufferMode = sslBufferMode; this.initializer = initializer; this.verifier = verifier; this.securePortStrategy = null; } public BasicServerTlsStrategy( final SSLContext sslContext, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this(sslContext, (SSLBufferMode) null, initializer, verifier); } public BasicServerTlsStrategy( final SSLContext sslContext, final SSLSessionVerifier verifier) { this(sslContext, (SSLBufferMode) null, null, verifier); } public BasicServerTlsStrategy(final SSLContext sslContext) { this(sslContext, null, null, null, null); } public BasicServerTlsStrategy() { this(SSLContexts.createSystemDefault()); } private boolean isApplicable(final SocketAddress localAddress) { return securePortStrategy == null || securePortStrategy.isSecure(localAddress); } @Override public void upgrade( final TransportSecurityLayer tlsSession, final NamedEndpoint endpoint, final Object attachment, final Timeout handshakeTimeout, final FutureCallback callback) { tlsSession.startTls(sslContext, endpoint, sslBufferMode, TlsSupport.enforceStrongSecurity(initializer), verifier, handshakeTimeout, callback); } /** * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)} */ @Deprecated @Override public boolean upgrade( final TransportSecurityLayer tlsSession, final HttpHost host, final SocketAddress localAddress, final SocketAddress remoteAddress, final Object attachment, final Timeout handshakeTimeout) { if (isApplicable(localAddress)) { upgrade(tlsSession, host, attachment, handshakeTimeout, null); return true; } return false; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/TlsSupport.java0100664 0000000 0000000 00000004074 14403631147 025156 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.ssl; import javax.net.ssl.SSLParameters; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.http.ssl.TlsCiphers; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; /** * HTTP/1.1 TLS support methods * * @since 5.0 */ public final class TlsSupport { public static SSLSessionInitializer enforceStrongSecurity(final SSLSessionInitializer initializer) { return (endpoint, sslEngine) -> { final SSLParameters sslParameters = sslEngine.getSSLParameters(); sslParameters.setProtocols(TLS.excludeWeak(sslParameters.getProtocols())); sslParameters.setCipherSuites(TlsCiphers.excludeWeak(sslParameters.getCipherSuites())); sslEngine.setSSLParameters(sslParameters); if (initializer != null) { initializer.initialize(endpoint, sslEngine); } }; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/TlsStrategy.java0100664 0000000 0000000 00000007152 14403631147 025304 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.ssl; import java.net.SocketAddress; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.util.Timeout; /** * TLS protocol upgrade strategy for non-blocking {@link TransportSecurityLayer} sessions. * * @since 5.0 */ public interface TlsStrategy { /** * Secures current session layer with TLS. * * @param sessionLayer the session layer * @param host the name of the opposite endpoint when given or {@code null} otherwise. * @param localAddress the address of the local endpoint. * @param remoteAddress the address of the remote endpoint. * @param attachment arbitrary object passes to the TLS session initialization code. * @param handshakeTimeout the timeout to use while performing the TLS handshake; may be {@code null}. * @return {@code true} if the session has been upgraded, {@code false} otherwise. * * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)} */ @Deprecated boolean upgrade( TransportSecurityLayer sessionLayer, HttpHost host, SocketAddress localAddress, SocketAddress remoteAddress, Object attachment, Timeout handshakeTimeout); /** * Secures current session layer with TLS. * * @param sessionLayer the session layer * @param endpoint the name of the opposite endpoint when applicable or {@code null} otherwise. * @param attachment arbitrary object passes to the TLS session initialization code. * @param handshakeTimeout the timeout to use while performing the TLS handshake; may be {@code null}. * @param callback Operation result callback. * * @since 5.2 */ default void upgrade( TransportSecurityLayer sessionLayer, NamedEndpoint endpoint, Object attachment, Timeout handshakeTimeout, FutureCallback callback) { upgrade(sessionLayer, new HttpHost(URIScheme.HTTPS.id, endpoint.getHostName(), endpoint.getPort()), null, null, attachment, handshakeTimeout); if (callback != null) { callback.completed(sessionLayer); } } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java0100664 0000000 0000000 00000011421 14245617503 027327 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; /** * AsyncServerRequestHandler represents a routine for processing of a specific group * of HTTP requests. Request execution filters are designed to take care of protocol * specific aspects, whereas individual request handlers are expected to take care * of application specific HTTP processing. The main purpose of a request handler * is to generate a response object with a content entity to be sent back to * the client in response to the given request. * * @param request representation. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface AsyncServerRequestHandler { /** * Response trigger that can be used to submit a final HTTP response * and terminate HTTP request processing. */ interface ResponseTrigger { /** * Sends an intermediate informational HTTP response to the client. * * @param response the intermediate (1xx) HTTP response * @param context the actual execution context. */ void sendInformation(HttpResponse response, HttpContext context) throws HttpException, IOException; /** * Sends a final HTTP response to the client. * * @param responseProducer the HTTP response message producer. * @param context the actual execution context. */ void submitResponse(AsyncResponseProducer responseProducer, HttpContext context) throws HttpException, IOException; /** * Pushes a request message head as a promise to deliver a response message. * * @param promise the request message header used as a promise. * @param context the actual execution context. * @param responseProducer the push response message producer. */ void pushPromise(HttpRequest promise, HttpContext context, AsyncPushProducer responseProducer) throws HttpException, IOException; } /** * Triggered to signal new incoming request. The handler can create a {@link AsyncRequestConsumer} based on * properties of the request head and entity details and let it process the request data stream. The request * handler will be used to generate an object that represents request data. * * @param request the incoming request head. * @param entityDetails the request entity details or {@code null} if the request * does not enclose an entity. * @param context the actual execution context. * @return the request handler. */ AsyncRequestConsumer prepare(HttpRequest request, EntityDetails entityDetails, HttpContext context) throws HttpException; /** * Triggered to handles the request object produced by the {@link AsyncRequestConsumer} returned * from the {@link #prepare(HttpRequest, EntityDetails, HttpContext)} method. The handler can choose * to send response messages immediately inside the call or asynchronously at some later point. * * @param requestObject the request object. * @param responseTrigger the response trigger. * @param context the actual execution context. */ void handle(T requestObject, ResponseTrigger responseTrigger, HttpContext context) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncResponseProducer.java0100664 0000000 0000000 00000004162 14245617503 026520 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract asynchronous response producer. * * @since 5.0 */ public interface AsyncResponseProducer extends AsyncDataProducer { /** * Triggered to signal the ability of the underlying response channel * to accept response messages. The data producer can choose to send * response messages immediately inside the call or asynchronously * at some later point. * * @param channel the response channel capable to accepting response messages. * @param context the actual execution context. */ void sendResponse(ResponseChannel channel, HttpContext context) throws HttpException, IOException; /** * Triggered to signal a failure in data generation. * * @param cause the cause of the failure. */ void failed(Exception cause); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncResponseConsumer.java0100664 0000000 0000000 00000005444 14245617503 026534 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract asynchronous response consumer. * * @param response representation. * * @since 5.0 */ public interface AsyncResponseConsumer extends AsyncDataConsumer { /** * Triggered to signal receipt of a response message head. * * @param response the response message head. * @param entityDetails the response entity details or {@code null} if the response * does not enclose an entity. * @param context the actual execution context. * @param resultCallback the result callback called when response processing * has been completed successfully or unsuccessfully. */ void consumeResponse(HttpResponse response, EntityDetails entityDetails, HttpContext context, FutureCallback resultCallback) throws HttpException, IOException; /** * Triggered to signal receipt of an intermediate (1xx) HTTP response. * * @param response the intermediate (1xx) HTTP response. * @param context the actual execution context. */ void informationResponse(HttpResponse response, HttpContext context) throws HttpException, IOException; /** * Triggered to signal a failure in data processing. * * @param cause the cause of the failure. */ void failed(Exception cause); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncFilterChain.java0100664 0000000 0000000 00000006656 14245617503 025420 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; /** * AsyncFilterChain represents a single element in the server side request processing chain. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface AsyncFilterChain { /** * Response trigger that can be used to generate the final HTTP response * and terminate HTTP request processing. */ interface ResponseTrigger { /** * Sends an intermediate informational HTTP response to the client. * * @param response the intermediate (1xx) HTTP response. */ void sendInformation(HttpResponse response) throws HttpException, IOException; /** * Sends a final HTTP response to the client. * * @param response the final (non 1xx) HTTP response. */ void submitResponse(HttpResponse response, AsyncEntityProducer entityProducer) throws HttpException, IOException; /** * Pushes a request message head as a promise to deliver a response message. * * @param promise the request message header used as a promise. * @param responseProducer the push response message producer. */ void pushPromise(HttpRequest promise, AsyncPushProducer responseProducer) throws HttpException, IOException; } /** * Proceeds to the next element in the request processing chain. * * @param request the actual request. * @param entityDetails the request entity details or {@code null} if the request * does not enclose an entity. * @param responseTrigger the response trigger. * @param context the actual execution context. */ AsyncDataConsumer proceed( HttpRequest request, EntityDetails entityDetails, HttpContext context, ResponseTrigger responseTrigger) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncClientExchangeHandler.java0100664 0000000 0000000 00000005756 14245617503 027407 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract asynchronous client side message exchange handler that acts as a request producer * and a response consumer. * * @since 5.0 */ public interface AsyncClientExchangeHandler extends AsyncDataExchangeHandler { /** * Triggered to signal the ability of the underlying request channel * to accept a request messages. The data producer can choose to send * a request message immediately inside the call or asynchronously * at some later point. * * @param channel the request channel capable to accepting a request message. * @param context the actual execution context. */ void produceRequest(RequestChannel channel, HttpContext context) throws HttpException, IOException; /** * Triggered to signal receipt of a response message head. * * @param response the response message head. * @param entityDetails the response entity details or {@code null} if the response * does not enclose an entity. * @param context the actual execution context. */ void consumeResponse(HttpResponse response, EntityDetails entityDetails, HttpContext context) throws HttpException, IOException; /** * Triggered to signal receipt of an intermediate (1xx) HTTP response. * * @param response the intermediate (1xx) HTTP response. * @param context the actual execution context. */ void consumeInformation(HttpResponse response, HttpContext context) throws HttpException, IOException; /** * Triggered to cancel the message exchange. */ void cancel(); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/RequestChannel.java0100664 0000000 0000000 00000004112 14245617503 025134 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract request channel. *

* Implementations are expected to be thread-safe. *

* @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public interface RequestChannel { /** * Sends a request through this channel. * * @param request the outgoing request. * @param entityDetails the details of the entity enclosed in the request * @param context the actual execution context. */ void sendRequest(HttpRequest request, EntityDetails entityDetails, HttpContext context) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/NHttpMessageParserFactory.java0100664 0000000 0000000 00000002666 14245617503 027276 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import org.apache.hc.core5.http.MessageHeaders; /** * Factory for {@link NHttpMessageParser} instances. * * @since 4.3 */ public interface NHttpMessageParserFactory { NHttpMessageParser create(); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncRequestConsumer.java0100664 0000000 0000000 00000004726 14245617503 026370 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract asynchronous request consumer. * * @param request representation. * * @since 5.0 */ public interface AsyncRequestConsumer extends AsyncDataConsumer { /** * Triggered to signal receipt of a request message head. * * @param request the request message head. * @param entityDetails the request entity details or {@code null} if the request * does not enclose an entity. * @param context the actual execution context. * @param resultCallback the result callback called when request processing * has been completed successfully or unsuccessfully. */ void consumeRequest(HttpRequest request, EntityDetails entityDetails, HttpContext context, FutureCallback resultCallback) throws HttpException, IOException; /** * Triggered to signal a failure in data processing. * * @param cause the cause of the failure. */ void failed(Exception cause); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/CapacityChannel.java0100664 0000000 0000000 00000003602 14245617503 025244 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Abstract capacity update channel. *

* Implementations are expected to be thread-safe. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public interface CapacityChannel { /** * Updates data capacity information through this channel. The total number of * bytes the consumer is capable of accepting is incremented * by the given increment number. * * @param increment non-negative number of extra bytes the consumer * can accept. */ void update(int increment) throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncFilterHandler.java0100664 0000000 0000000 00000006041 14245617503 025737 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; /** * AsyncFilterHandler represents a routine for handling all incoming requests * in the server side request processing chain. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface AsyncFilterHandler { /** * Processes the incoming HTTP request and if processing has been completed * submits a final response to the client. The handler can choose to send * response messages immediately inside the call or asynchronously at some later point. * The handler must not use the response trigger after passing control to the next filter * with the * {@link AsyncFilterChain#proceed(HttpRequest, EntityDetails, HttpContext, AsyncFilterChain.ResponseTrigger)} * method. * * @param request the actual request head. * @param entityDetails the request entity details or {@code null} if the request * does not enclose an entity. * @param context the actual execution context. * @param responseTrigger the response trigger. * @param chain the next element in the request processing chain. * @return the data consumer to be used to process incoming request data. It is * expected to be {@code null} if entityDetails parameter is {@code null}. */ AsyncDataConsumer handle( HttpRequest request, EntityDetails entityDetails, HttpContext context, AsyncFilterChain.ResponseTrigger responseTrigger, AsyncFilterChain chain) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/ResourceHolder.java0100664 0000000 0000000 00000003263 14245617503 025146 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Abstract resource holder. *

* Implementations are expected to ensure that {@link #releaseResources()} methods is idempotent and is * safe to invoke multiple times. *

*

* Implementations are expected to be thread-safe. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public interface ResourceHolder { void releaseResources(); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/NHttpMessageParser.java0100664 0000000 0000000 00000004355 14245617503 025743 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.MessageHeaders; /** * Message parser intended to build HTTP message head from a session input buffer.. * * @since 4.0 */ public interface NHttpMessageParser { /** * Resets the parser. The parser will be ready to start parsing another * HTTP message. */ void reset(); /** * Attempts to parse a complete message head from the content of the * internal buffer. If the message in the input buffer is incomplete * this method will return {@code null}. * * @param buffer session input buffer. * @param endOfStream end of stream flag * @return HTTP message head, if available, {@code null} otherwise. * @throws IOException in case of an I/O error. * @throws HttpException in case the HTTP message is malformed or * violates the HTTP protocol. */ T parse(SessionInputBuffer buffer, boolean endOfStream) throws IOException, HttpException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/0040775 0000000 0000000 00000000000 14435411677 022676 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/package-info.java0100664 0000000 0000000 00000002446 14245617503 026063 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP message entity APIs based on the asynchronous (non-blocking) I/O model. */ package org.apache.hc.core5.http.nio.entity; httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/StringAsyncEntityProducer.java0100664 0000000 0000000 00000006664 14245617503 030712 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.CharBuffer; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.nio.StreamChannel; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; /** * Basic {@link org.apache.hc.core5.http.nio.AsyncDataProducer} implementation that * generates data stream from content of a string. * * @since 5.0 */ public class StringAsyncEntityProducer extends AbstractCharAsyncEntityProducer { private final CharBuffer content; private final AtomicReference exception; public StringAsyncEntityProducer( final CharSequence content, final int bufferSize, final int fragmentSizeHint, final ContentType contentType) { super(bufferSize, fragmentSizeHint, contentType); Args.notNull(content, "Content"); this.content = CharBuffer.wrap(content); this.exception = new AtomicReference<>(); } public StringAsyncEntityProducer(final CharSequence content, final int bufferSize, final ContentType contentType) { this(content, bufferSize, -1, contentType); } public StringAsyncEntityProducer(final CharSequence content, final ContentType contentType) { this(content, 4096, contentType); } public StringAsyncEntityProducer(final CharSequence content) { this(content, ContentType.TEXT_PLAIN); } @Override public boolean isRepeatable() { return true; } @Override protected int availableData() { return Integer.MAX_VALUE; } @Override protected void produceData(final StreamChannel channel) throws IOException { Asserts.notNull(channel, "Channel"); channel.write(content); if (!content.hasRemaining()) { channel.endStream(); } } @Override public void failed(final Exception cause) { if (exception.compareAndSet(null, cause)) { releaseResources(); } } public Exception getException() { return exception.get(); } @Override public void releaseResources() { this.content.clear(); super.releaseResources(); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AbstractCharAsyncEntityConsumer.java0100664 0000000 0000000 00000007372 14403631147 032006 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.UnsupportedCharsetException; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.util.Args; /** * Abstract text entity content consumer. * * @since 5.0 * * @param entity representation. */ public abstract class AbstractCharAsyncEntityConsumer extends AbstractCharDataConsumer implements AsyncEntityConsumer { private volatile FutureCallback resultCallback; private volatile T content; protected AbstractCharAsyncEntityConsumer(final int bufSize, final CharCodingConfig charCodingConfig) { super(bufSize, charCodingConfig); } public AbstractCharAsyncEntityConsumer() { } /** * Triggered to signal beginning of entity content stream. * * @param contentType the entity content type */ protected abstract void streamStart(ContentType contentType) throws HttpException, IOException; /** * Triggered to generate entity representation. * * @return the entity content */ protected abstract T generateContent() throws IOException; @Override public final void streamStart( final EntityDetails entityDetails, final FutureCallback resultCallback) throws IOException, HttpException { Args.notNull(resultCallback, "Result callback"); this.resultCallback = resultCallback; try { final ContentType contentType = entityDetails != null ? ContentType.parse(entityDetails.getContentType()) : null; setCharset(ContentType.getCharset(contentType, null)); streamStart(contentType); } catch (final UnsupportedCharsetException ex) { throw new UnsupportedEncodingException(ex.getMessage()); } } @Override protected final void completed() throws IOException { content = generateContent(); if (resultCallback != null) { resultCallback.completed(content); } releaseResources(); } @Override public final void failed(final Exception cause) { if (resultCallback != null) { resultCallback.failed(cause); } releaseResources(); } @Override public final T getContent() { return content; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AbstractBinDataConsumer.java0100664 0000000 0000000 00000005430 14245617503 030235 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; /** * Abstract binary data consumer. * * @since 5.0 */ public abstract class AbstractBinDataConsumer implements AsyncDataConsumer { private static final ByteBuffer EMPTY = ByteBuffer.wrap(new byte[0]); /** * Triggered to obtain the capacity increment. * * @return the number of bytes this consumer is prepared to process. */ protected abstract int capacityIncrement(); /** * Triggered to pass incoming data packet to the data consumer. * * @param src the data packet. * @param endOfStream flag indicating whether this data packet is the last in the data stream. * */ protected abstract void data(ByteBuffer src, boolean endOfStream) throws IOException; /** * Triggered to signal completion of data processing. */ protected abstract void completed() throws IOException; @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(capacityIncrement()); } @Override public final void consume(final ByteBuffer src) throws IOException { data(src, false); } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { data(EMPTY, true); completed(); } }httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AsyncEntityProducers.java0100664 0000000 0000000 00000021544 14403631147 027674 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.StreamChannel; import org.apache.hc.core5.net.WWWFormCodec; /** * {AsyncEntityProducer} factory methods. * * @since 5.0 */ public final class AsyncEntityProducers { private AsyncEntityProducers() { } public static AsyncEntityProducer create(final String content, final ContentType contentType) { return new BasicAsyncEntityProducer(content, contentType); } public static AsyncEntityProducer create(final String content, final Charset charset) { return new BasicAsyncEntityProducer(content, ContentType.TEXT_PLAIN.withCharset(charset)); } public static AsyncEntityProducer create(final String content) { return new BasicAsyncEntityProducer(content, ContentType.TEXT_PLAIN); } public static AsyncEntityProducer create(final byte[] content, final ContentType contentType) { return new BasicAsyncEntityProducer(content, contentType); } public static AsyncEntityProducer create(final File content, final ContentType contentType) { return new FileEntityProducer(content, contentType); } public static AsyncEntityProducer createUrlEncoded( final Iterable parameters, final Charset charset) { final ContentType contentType = charset != null ? ContentType.APPLICATION_FORM_URLENCODED.withCharset(charset) : ContentType.APPLICATION_FORM_URLENCODED; return create(WWWFormCodec.format(parameters, contentType.getCharset()), contentType); } public static AsyncEntityProducer createBinary( final Callback> callback, final ContentType contentType) { return new AbstractBinAsyncEntityProducer(0, contentType) { @Override protected int availableData() { return Integer.MAX_VALUE; } @Override protected void produceData(final StreamChannel channel) throws IOException { callback.execute(channel); } @Override public boolean isRepeatable() { return false; } @Override public void failed(final Exception cause) { } }; } public static AsyncEntityProducer createText( final Callback> callback, final ContentType contentType) { return new AbstractCharAsyncEntityProducer(4096, 2048, contentType) { @Override protected int availableData() { return Integer.MAX_VALUE; } @Override protected void produceData(final StreamChannel channel) throws IOException { callback.execute(channel); } @Override public boolean isRepeatable() { return false; } @Override public void failed(final Exception cause) { } }; } public static AsyncEntityProducer withTrailers(final AsyncEntityProducer entity, final Header... trailers) { return new AsyncEntityProducerWrapper(entity) { @Override public boolean isChunked() { // Must be chunk coded return true; } @Override public long getContentLength() { return -1; } @Override public Set getTrailerNames() { final Set names = new LinkedHashSet<>(); for (final Header trailer: trailers) { names.add(trailer.getName()); } return names; } @Override public void produce(final DataStreamChannel channel) throws IOException { super.produce(new DataStreamChannel() { @Override public void requestOutput() { channel.requestOutput(); } @Override public int write(final ByteBuffer src) throws IOException { return channel.write(src); } @Override public void endStream(final List p) throws IOException { final List
allTrailers; if (p != null && !p.isEmpty()) { allTrailers = new ArrayList<>(p); allTrailers.addAll(Arrays.asList(trailers)); } else { allTrailers = Arrays.asList(trailers); } channel.endStream(allTrailers); } @Override public void endStream() throws IOException { channel.endStream(); } }); } }; } public static AsyncEntityProducer create(final String content, final ContentType contentType, final Header... trailers) { return withTrailers(create(content, contentType), trailers); } public static AsyncEntityProducer create(final String content, final Charset charset, final Header... trailers) { return withTrailers(create(content, charset), trailers); } public static AsyncEntityProducer create(final String content, final Header... trailers) { return withTrailers(create(content), trailers); } public static AsyncEntityProducer create(final byte[] content, final ContentType contentType, final Header... trailers) { return withTrailers(create(content, contentType), trailers); } public static AsyncEntityProducer create(final File content, final ContentType contentType, final Header... trailers) { return withTrailers(create(content, contentType), trailers); } /** * @since 5.2 */ public static AsyncEntityProducer create(final Path content, final ContentType contentType, final Header... trailers) throws IOException { return withTrailers(new PathEntityProducer(content, contentType, StandardOpenOption.READ), trailers); } /** * @since 5.2 */ public static AsyncEntityProducer create(final Path content, final ContentType contentType, final OpenOption... options) throws IOException { return new PathEntityProducer(content, contentType, options); } public static AsyncEntityProducer createBinary( final Callback> callback, final ContentType contentType, final Header... trailers) { return withTrailers(createBinary(callback, contentType), trailers); } public static AsyncEntityProducer createText( final Callback> callback, final ContentType contentType, final Header... trailers) { return withTrailers(createText(callback, contentType), trailers); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/DigestingEntityConsumer.java0100664 0000000 0000000 00000007762 14245617503 030373 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.util.Args; /** * {@link AsyncEntityConsumer} decorator that calculates a digest hash from * the data stream content and keeps the list of trailers received with * the data stream. * * @since 5.0 */ public class DigestingEntityConsumer implements AsyncEntityConsumer { private final AsyncEntityConsumer wrapped; private final List
trailers; private final MessageDigest digester; private volatile byte[] digest; public DigestingEntityConsumer( final String algo, final AsyncEntityConsumer wrapped) throws NoSuchAlgorithmException { this.wrapped = Args.notNull(wrapped, "Entity consumer"); this.trailers = new ArrayList<>(); this.digester = MessageDigest.getInstance(algo); } @Override public void streamStart( final EntityDetails entityDetails, final FutureCallback resultCallback) throws IOException, HttpException { wrapped.streamStart(entityDetails, resultCallback); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { wrapped.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { src.mark(); digester.update(src); src.reset(); wrapped.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { if (trailers != null) { this.trailers.addAll(trailers); } digest = digester.digest(); wrapped.streamEnd(trailers); } @Override public void failed(final Exception cause) { wrapped.failed(cause); } @Override public T getContent() { return wrapped.getContent(); } @Override public void releaseResources() { wrapped.releaseResources(); } /** * List of trailers sent with the data stream. * * @return the list of trailers sent with the data stream */ public List
getTrailers() { return trailers != null ? new ArrayList<>(trailers) : null; } /** * Returns digest hash. * * @return the digest hash value. */ public byte[] getDigest() { return digest; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AbstractBinAsyncEntityConsumer.java0100664 0000000 0000000 00000006653 14245617503 031646 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.UnsupportedCharsetException; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.util.Args; /** * Abstract binary entity content consumer. * * @since 5.0 * * @param entity representation. */ public abstract class AbstractBinAsyncEntityConsumer extends AbstractBinDataConsumer implements AsyncEntityConsumer { private volatile FutureCallback resultCallback; private volatile T content; /** * Triggered to signal beginning of entity content stream. * * @param contentType the entity content type */ protected abstract void streamStart(ContentType contentType) throws HttpException, IOException; /** * Triggered to generate entity representation. * * @return the entity content */ protected abstract T generateContent() throws IOException; @Override public final void streamStart( final EntityDetails entityDetails, final FutureCallback resultCallback) throws IOException, HttpException { Args.notNull(resultCallback, "Result callback"); this.resultCallback = resultCallback; try { final ContentType contentType = entityDetails != null ? ContentType.parse(entityDetails.getContentType()) : null; streamStart(contentType); } catch (final UnsupportedCharsetException ex) { throw new UnsupportedEncodingException(ex.getMessage()); } } @Override protected final void completed() throws IOException { content = generateContent(); if (resultCallback != null) { resultCallback.completed(content); } releaseResources(); } @Override public final void failed(final Exception cause) { if (resultCallback != null) { resultCallback.failed(cause); } releaseResources(); } @Override public final T getContent() { return content; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AbstractBinAsyncEntityProducer.java0100664 0000000 0000000 00000015245 14435411677 031640 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Set; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.StreamChannel; import org.apache.hc.core5.util.Args; /** * Abstract binary entity content producer. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public abstract class AbstractBinAsyncEntityProducer implements AsyncEntityProducer { enum State { ACTIVE, FLUSHING, END_STREAM } private final int fragmentSizeHint; private final ByteBuffer byteBuffer; private final ContentType contentType; private volatile State state; public AbstractBinAsyncEntityProducer(final int fragmentSizeHint, final ContentType contentType) { this.fragmentSizeHint = fragmentSizeHint >= 0 ? fragmentSizeHint : 0; this.byteBuffer = ByteBuffer.allocate(this.fragmentSizeHint); this.contentType = contentType; this.state = State.ACTIVE; } private void flush(final StreamChannel channel) throws IOException { if (byteBuffer.position() > 0) { byteBuffer.flip(); channel.write(byteBuffer); byteBuffer.compact(); } } final int writeData(final StreamChannel channel, final ByteBuffer src) throws IOException { final int chunk = src.remaining(); if (chunk == 0) { return 0; } if (chunk > fragmentSizeHint) { // the data chunk is greater than the fragment hint // attempt to write it out to the channel directly // flush the buffer if not empty flush(channel); if (byteBuffer.position() == 0) { return channel.write(src); } } else { // the data chunk is smaller than the fragment hint // attempt to buffer it // flush the buffer if there is not enough space to store the chunk if (byteBuffer.remaining() < chunk) { flush(channel); } if (byteBuffer.remaining() >= chunk) { byteBuffer.put(src); if (!byteBuffer.hasRemaining()) { flush(channel); } return chunk; } } return 0; } final void streamEnd(final StreamChannel channel) throws IOException { if (state == State.ACTIVE) { state = State.FLUSHING; flush(channel); if (byteBuffer.position() == 0) { state = State.END_STREAM; channel.endStream(); } } } /** * Returns the number of bytes immediately available for output. * This method can be used as a hint to control output events * of the underlying I/O session. * * @return the number of bytes immediately available for output */ protected abstract int availableData(); /** * Triggered to signal the ability of the underlying byte channel * to accept more data. The data producer can choose to write data * immediately inside the call or asynchronously at some later point. *

* {@link StreamChannel} passed to this method is threading-safe. * * @param channel the data channel capable to accepting more data. */ protected abstract void produceData(StreamChannel channel) throws IOException; @Override public final String getContentType() { return contentType != null ? contentType.toString() : null; } @Override public String getContentEncoding() { return null; } @Override public boolean isChunked() { return false; } @Override public Set getTrailerNames() { return null; } @Override public long getContentLength() { return -1; } @Override public final int available() { if (state == State.ACTIVE) { return availableData(); } else { synchronized (byteBuffer) { return byteBuffer.position(); } } } @Override public final void produce(final DataStreamChannel channel) throws IOException { synchronized (byteBuffer) { if (state == State.ACTIVE) { produceData(new StreamChannel() { @Override public int write(final ByteBuffer src) throws IOException { Args.notNull(src, "Buffer"); synchronized (byteBuffer) { return writeData(channel, src); } } @Override public void endStream() throws IOException { synchronized (byteBuffer) { streamEnd(channel); } } }); } if (state == State.FLUSHING) { flush(channel); if (byteBuffer.position() == 0) { state = State.END_STREAM; channel.endStream(); } } } } @Override public void releaseResources() { state = State.ACTIVE; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/PathEntityProducer.java0100664 0000000 0000000 00000012522 14435411677 027335 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SeekableByteChannel; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; /** * {@link AsyncEntityProducer} implementation that generates a data stream from the content at a {@link Path}. * * @since 5.2 */ public final class PathEntityProducer implements AsyncEntityProducer { private static final int BUFFER_SIZE = 8192; private final Path file; private final OpenOption[] openOptions; private final ByteBuffer byteBuffer; private final long length; private final ContentType contentType; private final boolean chunked; private final AtomicReference exception; private final AtomicReference channelRef; private boolean eof; public PathEntityProducer(final Path file, final ContentType contentType, final boolean chunked, final OpenOption... openOptions) throws IOException { this(file, BUFFER_SIZE, contentType, chunked, openOptions); } public PathEntityProducer(final Path file, final ContentType contentType, final OpenOption... openOptions) throws IOException { this(file, contentType, false, openOptions); } public PathEntityProducer(final Path file, final int bufferSize, final ContentType contentType, final boolean chunked, final OpenOption... openOptions) throws IOException { this.file = Args.notNull(file, "file"); this.openOptions = openOptions; this.length = Files.size(file); this.byteBuffer = ByteBuffer.allocate(bufferSize); this.contentType = contentType; this.chunked = chunked; this.channelRef = new AtomicReference<>(); this.exception = new AtomicReference<>(); } public PathEntityProducer(final Path file, final OpenOption... openOptions) throws IOException { this(file, ContentType.APPLICATION_OCTET_STREAM, openOptions); } @Override public int available() { return Integer.MAX_VALUE; } @Override public void failed(final Exception cause) { if (exception.compareAndSet(null, cause)) { releaseResources(); } } @Override public String getContentEncoding() { return null; } @Override public long getContentLength() { return length; } @Override public String getContentType() { return contentType != null ? contentType.toString() : null; } public Exception getException() { return exception.get(); } @Override public Set getTrailerNames() { return null; } @Override public boolean isChunked() { return chunked; } @Override public boolean isRepeatable() { return true; } @Override public void produce(final DataStreamChannel dataStreamChannel) throws IOException { SeekableByteChannel seekableByteChannel = channelRef.get(); if (seekableByteChannel == null) { seekableByteChannel = Files.newByteChannel(file, openOptions); Asserts.check(channelRef.getAndSet(seekableByteChannel) == null, "Illegal producer state"); } if (!eof) { final int bytesRead = seekableByteChannel.read(byteBuffer); if (bytesRead < 0) { eof = true; } } if (byteBuffer.position() > 0) { byteBuffer.flip(); dataStreamChannel.write(byteBuffer); byteBuffer.compact(); } if (eof && byteBuffer.position() == 0) { dataStreamChannel.endStream(); releaseResources(); } } @Override public void releaseResources() { eof = false; Closer.closeQuietly(channelRef.getAndSet(null)); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/StringAsyncEntityConsumer.java0100664 0000000 0000000 00000006471 14325247343 030716 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.CharBuffer; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Basic {@link org.apache.hc.core5.http.nio.AsyncEntityConsumer} implementation * that processes the data stream content into a string. * * @since 5.0 */ public class StringAsyncEntityConsumer extends AbstractCharAsyncEntityConsumer { private final int capacityIncrement; private final CharArrayBuffer content; public StringAsyncEntityConsumer(final int bufSize, final int capacityIncrement, final CharCodingConfig charCodingConfig) { super(bufSize, charCodingConfig); this.capacityIncrement = Args.positive(capacityIncrement, "Capacity increment"); this.content = new CharArrayBuffer(1024); } public StringAsyncEntityConsumer(final int capacityIncrement) { this(DEF_BUF_SIZE, capacityIncrement, CharCodingConfig.DEFAULT); } public StringAsyncEntityConsumer(final CharCodingConfig charCodingConfig) { this(DEF_BUF_SIZE, Integer.MAX_VALUE, charCodingConfig); } public StringAsyncEntityConsumer() { this(Integer.MAX_VALUE); } @Override protected final void streamStart(final ContentType contentType) throws HttpException, IOException { } @Override protected int capacityIncrement() { final int available = content.capacity() - content.length(); return Math.max(capacityIncrement, available); } @Override protected final void data(final CharBuffer src, final boolean endOfStream) { Args.notNull(src, "CharBuffer"); final int chunk = src.remaining(); content.ensureCapacity(chunk); src.get(content.array(), content.length(), chunk); content.setLength(content.length() + chunk); } @Override public String generateContent() { return content.toString(); } @Override public void releaseResources() { content.clear(); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AbstractCharDataConsumer.java0100664 0000000 0000000 00000012552 14435411677 030412 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CoderResult; import java.nio.charset.StandardCharsets; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.util.Args; /** * Abstract text data consumer. * * @since 5.0 */ public abstract class AbstractCharDataConsumer implements AsyncDataConsumer { protected static final int DEF_BUF_SIZE = 8192; private static final ByteBuffer EMPTY_BIN = ByteBuffer.wrap(new byte[0]); private final CharBuffer charBuffer; private final CharCodingConfig charCodingConfig; private volatile Charset charset; private volatile CharsetDecoder charsetDecoder; protected AbstractCharDataConsumer(final int bufSize, final CharCodingConfig charCodingConfig) { this.charBuffer = CharBuffer.allocate(Args.positive(bufSize, "Buffer size")); this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; } public AbstractCharDataConsumer() { this(DEF_BUF_SIZE, CharCodingConfig.DEFAULT); } /** * Triggered to obtain the capacity increment. * * @return the number of bytes this consumer is prepared to process. */ protected abstract int capacityIncrement(); /** * Triggered to pass incoming data packet to the data consumer. * * @param src the data packet. * @param endOfStream flag indicating whether this data packet is the last in the data stream. * */ protected abstract void data(CharBuffer src, boolean endOfStream) throws IOException; /** * Triggered to signal completion of data processing. */ protected abstract void completed() throws IOException; protected final void setCharset(final Charset charset) { this.charset = charset != null ? charset : charCodingConfig.getCharset(); this.charsetDecoder = null; } @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(capacityIncrement()); } private void checkResult(final CoderResult result) throws IOException { if (result.isError()) { result.throwException(); } } private void doDecode(final boolean endOfStream) throws IOException { charBuffer.flip(); data(charBuffer, endOfStream); charBuffer.clear(); } private CharsetDecoder getCharsetDecoder() { if (charsetDecoder == null) { Charset charset = this.charset; if (charset == null) { charset = charCodingConfig.getCharset(); } if (charset == null) { charset = StandardCharsets.US_ASCII; } charsetDecoder = charset.newDecoder(); if (charCodingConfig.getMalformedInputAction() != null) { charsetDecoder.onMalformedInput(charCodingConfig.getMalformedInputAction()); } if (charCodingConfig.getUnmappableInputAction() != null) { charsetDecoder.onUnmappableCharacter(charCodingConfig.getUnmappableInputAction()); } } return charsetDecoder; } @Override public final void consume(final ByteBuffer src) throws IOException { final CharsetDecoder charsetDecoder = getCharsetDecoder(); while (src.hasRemaining()) { checkResult(charsetDecoder.decode(src, charBuffer, false)); doDecode(false); } } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { final CharsetDecoder charsetDecoder = getCharsetDecoder(); checkResult(charsetDecoder.decode(EMPTY_BIN, charBuffer, true)); doDecode(false); checkResult(charsetDecoder.flush(charBuffer)); doDecode(true); completed(); } }httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/FileEntityProducer.java0100664 0000000 0000000 00000011476 14435411677 027327 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; /** * {@link AsyncEntityProducer} implementation that generates data stream * from content of a {@link File}. * * @since 5.0 */ public final class FileEntityProducer implements AsyncEntityProducer { private final File file; private final ByteBuffer byteBuffer; private final long length; private final ContentType contentType; private final boolean chunked; private final AtomicReference exception; private final AtomicReference accessFileRef; private boolean eof; public FileEntityProducer(final File file, final int bufferSize, final ContentType contentType, final boolean chunked) { this.file = Args.notNull(file, "File"); this.length = file.length(); this.byteBuffer = ByteBuffer.allocate(bufferSize); this.contentType = contentType; this.chunked = chunked; this.accessFileRef = new AtomicReference<>(); this.exception = new AtomicReference<>(); } public FileEntityProducer(final File file, final ContentType contentType, final boolean chunked) { this(file, 8192, contentType, chunked); } public FileEntityProducer(final File file, final ContentType contentType) { this(file, contentType, false); } public FileEntityProducer(final File file) { this(file, ContentType.APPLICATION_OCTET_STREAM); } @Override public boolean isRepeatable() { return true; } @Override public String getContentType() { return contentType != null ? contentType.toString() : null; } @Override public long getContentLength() { return length; } @Override public int available() { return Integer.MAX_VALUE; } @Override public String getContentEncoding() { return null; } @Override public boolean isChunked() { return chunked; } @Override public Set getTrailerNames() { return null; } @Override public void produce(final DataStreamChannel channel) throws IOException { @SuppressWarnings("resource") RandomAccessFile accessFile = accessFileRef.get(); if (accessFile == null) { accessFile = new RandomAccessFile(file, "r"); Asserts.check(accessFileRef.getAndSet(accessFile) == null, "Illegal producer state"); } if (!eof) { final int bytesRead = accessFile.getChannel().read(byteBuffer); if (bytesRead < 0) { eof = true; } } if (byteBuffer.position() > 0) { byteBuffer.flip(); channel.write(byteBuffer); byteBuffer.compact(); } if (eof && byteBuffer.position() == 0) { channel.endStream(); releaseResources(); } } @Override public void failed(final Exception cause) { if (exception.compareAndSet(null, cause)) { releaseResources(); } } public Exception getException() { return exception.get(); } @Override public void releaseResources() { eof = false; Closer.closeQuietly(accessFileRef.getAndSet(null)); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/NoopEntityConsumer.java0100664 0000000 0000000 00000005520 14403631147 027353 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; /** * No-op {@link AsyncEntityConsumer} that discards all data from the data stream. * * @since 5.0 * * @deprecated Use {@link DiscardingEntityConsumer} */ @Deprecated public final class NoopEntityConsumer implements AsyncEntityConsumer { private volatile FutureCallback resultCallback; @Override public void streamStart( final EntityDetails entityDetails, final FutureCallback resultCallback) throws IOException, HttpException { this.resultCallback = resultCallback; } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(Integer.MAX_VALUE); } @Override public void consume(final ByteBuffer src) throws IOException { } @Override public void streamEnd(final List trailers) throws IOException { if (resultCallback != null) { resultCallback.completed(null); } } @Override public void failed(final Exception cause) { if (resultCallback != null) { resultCallback.failed(cause); } } @Override public Void getContent() { return null; } @Override public void releaseResources() { } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AbstractCharAsyncEntityProducer.java0100664 0000000 0000000 00000016740 14435411677 032006 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.nio.charset.StandardCharsets; import java.util.Set; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.StreamChannel; import org.apache.hc.core5.util.Args; /** * Abstract text entity content producer. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public abstract class AbstractCharAsyncEntityProducer implements AsyncEntityProducer { private static final CharBuffer EMPTY = CharBuffer.wrap(new char[0]); enum State { ACTIVE, FLUSHING, END_STREAM } private final ByteBuffer bytebuf; private final int fragmentSizeHint; private final ContentType contentType; private final CharsetEncoder charsetEncoder; private volatile State state; public AbstractCharAsyncEntityProducer( final int bufferSize, final int fragmentSizeHint, final ContentType contentType) { Args.positive(bufferSize, "Buffer size"); this.fragmentSizeHint = fragmentSizeHint >= 0 ? fragmentSizeHint : 0; this.bytebuf = ByteBuffer.allocate(bufferSize); this.contentType = contentType; final Charset charset = ContentType.getCharset(contentType, StandardCharsets.US_ASCII); this.charsetEncoder = charset.newEncoder(); this.state = State.ACTIVE; } private void flush(final StreamChannel channel) throws IOException { if (bytebuf.position() > 0) { bytebuf.flip(); channel.write(bytebuf); bytebuf.compact(); } } final int writeData(final StreamChannel channel, final CharBuffer src) throws IOException { final int chunk = src.remaining(); if (chunk == 0) { return 0; } final int p = src.position(); final CoderResult result = charsetEncoder.encode(src, bytebuf, false); if (result.isError()) { result.throwException(); } if (!bytebuf.hasRemaining() || bytebuf.position() >= fragmentSizeHint) { flush(channel); } return src.position() - p; } final void streamEnd(final StreamChannel channel) throws IOException { if (state == State.ACTIVE) { state = State.FLUSHING; if (!bytebuf.hasRemaining()) { flush(channel); } final CoderResult result = charsetEncoder.encode(EMPTY, bytebuf, true); if (result.isError()) { result.throwException(); } final CoderResult result2 = charsetEncoder.flush(bytebuf); if (result2.isError()) { result.throwException(); } else if (result.isUnderflow()) { flush(channel); if (bytebuf.position() == 0) { state = State.END_STREAM; channel.endStream(); } } } } /** * Returns the number of bytes immediately available for output. * This method can be used as a hint to control output events * of the underlying I/O session. * * @return the number of bytes immediately available for output */ protected abstract int availableData(); /** * Triggered to signal the ability of the underlying char channel * to accept more data. The data producer can choose to write data * immediately inside the call or asynchronously at some later point. *

* {@link StreamChannel} passed to this method is threading-safe. * * @param channel the data channel capable to accepting more data. */ protected abstract void produceData(StreamChannel channel) throws IOException; @Override public final String getContentType() { return contentType != null ? contentType.toString() : null; } @Override public String getContentEncoding() { return null; } @Override public long getContentLength() { return -1; } @Override public boolean isChunked() { return false; } @Override public Set getTrailerNames() { return null; } @Override public final int available() { if (state == State.ACTIVE) { return availableData(); } else { synchronized (bytebuf) { return bytebuf.position(); } } } @Override public final void produce(final DataStreamChannel channel) throws IOException { synchronized (bytebuf) { if (state == State.ACTIVE) { produceData(new StreamChannel() { @Override public int write(final CharBuffer src) throws IOException { Args.notNull(src, "Buffer"); synchronized (bytebuf) { return writeData(channel, src); } } @Override public void endStream() throws IOException { synchronized (bytebuf) { streamEnd(channel); } } }); } if (state == State.FLUSHING) { final CoderResult result = charsetEncoder.flush(bytebuf); if (result.isError()) { result.throwException(); } else if (result.isOverflow()) { flush(channel); } else if (result.isUnderflow()) { flush(channel); if (bytebuf.position() == 0) { state = State.END_STREAM; channel.endStream(); } } } } } @Override public void releaseResources() { state = State.ACTIVE; charsetEncoder.reset(); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/BasicAsyncEntityProducer.java0100664 0000000 0000000 00000011216 14435411677 030457 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.util.Args; /** * Basic {@link AsyncEntityProducer} implementation that generates data stream * from content of a byte array. * * @since 5.0 */ public class BasicAsyncEntityProducer implements AsyncEntityProducer { private final ByteBuffer bytebuf; private final int length; private final ContentType contentType; private final boolean chunked; private final AtomicReference exception; public BasicAsyncEntityProducer(final byte[] content, final ContentType contentType, final boolean chunked) { Args.notNull(content, "Content"); this.bytebuf = ByteBuffer.wrap(content); this.length = this.bytebuf.remaining(); this.contentType = contentType; this.chunked = chunked; this.exception = new AtomicReference<>(); } public BasicAsyncEntityProducer(final byte[] content, final ContentType contentType) { this(content, contentType, false); } public BasicAsyncEntityProducer(final byte[] content) { this(content, ContentType.APPLICATION_OCTET_STREAM); } public BasicAsyncEntityProducer(final CharSequence content, final ContentType contentType, final boolean chunked) { Args.notNull(content, "Content"); this.contentType = contentType; final Charset charset = ContentType.getCharset(contentType, StandardCharsets.US_ASCII); this.bytebuf = charset.encode(CharBuffer.wrap(content)); this.length = this.bytebuf.remaining(); this.chunked = chunked; this.exception = new AtomicReference<>(); } public BasicAsyncEntityProducer(final CharSequence content, final ContentType contentType) { this(content, contentType, false); } public BasicAsyncEntityProducer(final CharSequence content) { this(content, ContentType.TEXT_PLAIN); } @Override public boolean isRepeatable() { return true; } @Override public final String getContentType() { return contentType != null ? contentType.toString() : null; } @Override public long getContentLength() { return length; } @Override public int available() { return Integer.MAX_VALUE; } @Override public String getContentEncoding() { return null; } @Override public boolean isChunked() { return chunked; } @Override public Set getTrailerNames() { return null; } @Override public final void produce(final DataStreamChannel channel) throws IOException { if (bytebuf.hasRemaining()) { channel.write(bytebuf); } if (!bytebuf.hasRemaining()) { channel.endStream(); } } @Override public final void failed(final Exception cause) { if (exception.compareAndSet(null, cause)) { releaseResources(); } } public final Exception getException() { return exception.get(); } @Override public void releaseResources() { bytebuf.clear(); bytebuf.limit(length); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/BasicAsyncEntityConsumer.java0100664 0000000 0000000 00000004540 14245617503 030464 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.util.ByteArrayBuffer; /** * Basic {@link org.apache.hc.core5.http.nio.AsyncEntityConsumer} implementation * that processes the data stream content into a byte array. * * @since 5.0 */ public class BasicAsyncEntityConsumer extends AbstractBinAsyncEntityConsumer { private final ByteArrayBuffer buffer; public BasicAsyncEntityConsumer() { super(); this.buffer = new ByteArrayBuffer(1024); } @Override protected void streamStart(final ContentType contentType) throws HttpException, IOException { } @Override protected int capacityIncrement() { return Integer.MAX_VALUE; } @Override protected void data(final ByteBuffer src, final boolean endOfStream) throws IOException { buffer.append(src); } @Override protected byte[] generateContent() throws IOException { return buffer.toByteArray(); } @Override public void releaseResources() { buffer.clear(); } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/DigestingEntityProducer.java0100664 0000000 0000000 00000012175 14245617503 030355 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * {@link AsyncEntityProducer} decorator that calculates a digest hash from * the data stream content and appends its value to the list of trailers. * * @since 5.0 */ public class DigestingEntityProducer implements AsyncEntityProducer { private final AsyncEntityProducer wrapped; private final MessageDigest digester; private volatile byte[] digest; public DigestingEntityProducer( final String algo, final AsyncEntityProducer wrapped) { this.wrapped = Args.notNull(wrapped, "Entity consumer"); try { this.digester = MessageDigest.getInstance(algo); } catch (final NoSuchAlgorithmException ex) { throw new IllegalArgumentException("Unsupported digest algorithm: " + algo); } } @Override public boolean isRepeatable() { return wrapped.isRepeatable(); } @Override public long getContentLength() { return wrapped.getContentLength(); } @Override public String getContentType() { return wrapped.getContentType(); } @Override public String getContentEncoding() { return wrapped.getContentEncoding(); } @Override public boolean isChunked() { return wrapped.isChunked(); } @Override public Set getTrailerNames() { final Set allNames = new LinkedHashSet<>(); final Set names = wrapped.getTrailerNames(); if (names != null) { allNames.addAll(names); } allNames.add("digest-algo"); allNames.add("digest"); return allNames; } @Override public int available() { return wrapped.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { wrapped.produce(new DataStreamChannel() { @Override public void requestOutput() { channel.requestOutput(); } @Override public int write(final ByteBuffer src) throws IOException { final ByteBuffer dup = src.duplicate(); final int writtenBytes = channel.write(src); if (writtenBytes > 0) { dup.limit(dup.position() + writtenBytes); digester.update(dup); } return writtenBytes; } @Override public void endStream(final List trailers) throws IOException { digest = digester.digest(); final List

allTrailers = new ArrayList<>(); if (trailers != null) { allTrailers.addAll(trailers); } allTrailers.add(new BasicHeader("digest-algo", digester.getAlgorithm())); allTrailers.add(new BasicHeader("digest", TextUtils.toHexString(digest))); channel.endStream(allTrailers); } @Override public void endStream() throws IOException { endStream(null); } }); } @Override public void failed(final Exception cause) { wrapped.failed(cause); } @Override public void releaseResources() { wrapped.releaseResources(); } /** * Returns digest hash. * * @return the digest hash value. */ public byte[] getDigest() { return digest; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/DiscardingEntityConsumer.java0100664 0000000 0000000 00000005412 14403631147 030507 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; /** * No-op {@link AsyncEntityConsumer} that discards all data from the data stream. * * @since 5.2 */ public final class DiscardingEntityConsumer implements AsyncEntityConsumer { private volatile FutureCallback resultCallback; @Override public void streamStart( final EntityDetails entityDetails, final FutureCallback resultCallback) throws IOException, HttpException { this.resultCallback = resultCallback; } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(Integer.MAX_VALUE); } @Override public void consume(final ByteBuffer src) throws IOException { } @Override public void streamEnd(final List trailers) throws IOException { if (resultCallback != null) { resultCallback.completed(null); } } @Override public void failed(final Exception cause) { if (resultCallback != null) { resultCallback.failed(cause); } } @Override public T getContent() { return null; } @Override public void releaseResources() { } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AsyncEntityProducerWrapper.java0100664 0000000 0000000 00000006615 14245617503 031060 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.util.Set; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.util.Args; /** * Base class for wrapping entity producers that delegates all calls to the wrapped producer. * Implementations can derive from this class and override only those methods that * should not be delegated to the wrapped producer. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class AsyncEntityProducerWrapper implements AsyncEntityProducer { private final AsyncEntityProducer wrappedEntityProducer; public AsyncEntityProducerWrapper(final AsyncEntityProducer wrappedEntityProducer) { super(); this.wrappedEntityProducer = Args.notNull(wrappedEntityProducer, "Wrapped entity producer"); } @Override public boolean isRepeatable() { return wrappedEntityProducer.isRepeatable(); } @Override public boolean isChunked() { return wrappedEntityProducer.isChunked(); } @Override public long getContentLength() { return wrappedEntityProducer.getContentLength(); } @Override public String getContentType() { return wrappedEntityProducer.getContentType(); } @Override public String getContentEncoding() { return wrappedEntityProducer.getContentEncoding(); } @Override public Set getTrailerNames() { return wrappedEntityProducer.getTrailerNames(); } @Override public int available() { return wrappedEntityProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { wrappedEntityProducer.produce(channel); } @Override public void failed(final Exception cause) { wrappedEntityProducer.failed(cause); } @Override public void releaseResources() { wrappedEntityProducer.releaseResources(); } @Override public String toString() { return "Wrapper [" + wrappedEntityProducer + "]"; } } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncEntityProducer.java0100664 0000000 0000000 00000003372 14245617503 026200 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import org.apache.hc.core5.http.EntityDetails; /** * Abstract asynchronous message entity producer. * * @since 5.0 */ public interface AsyncEntityProducer extends AsyncDataProducer, EntityDetails { /** * Determines whether the producer can consistently produce the same content * after invocation of {@link ResourceHolder#releaseResources()}. */ boolean isRepeatable(); /** * Triggered to signal a failure in data generation. * * @param cause the cause of the failure. */ void failed(Exception cause); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncPushConsumer.java0100664 0000000 0000000 00000004601 14245617503 025647 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract asynchronous push response consumer. * * @since 5.0 */ public interface AsyncPushConsumer extends AsyncDataConsumer { /** * Triggered to signal receipt of a request message head used as a promise * and the corresponding pushed response. * * @param promise the request message head used as a promise. * @param response the pushed response message. * @param entityDetails the response entity details or {@code null} if the response * does not enclose an entity. * @param context the actual execution context. */ void consumePromise(HttpRequest promise, HttpResponse response, EntityDetails entityDetails, HttpContext context) throws HttpException, IOException; /** * Triggered to signal a failure in data processing. * * @param cause the cause of the failure. */ void failed(Exception cause); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataConsumer.java0100664 0000000 0000000 00000005137 14245617503 025606 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; /** * Abstract asynchronous data consumer. * * @since 5.0 */ public interface AsyncDataConsumer extends ResourceHolder { /** * Triggered to signal ability of the underlying data stream to receive * data capacity update. The data consumer can choose to write data * immediately inside the call or asynchronously at some later point. * * @param capacityChannel the channel for capacity updates. */ void updateCapacity(CapacityChannel capacityChannel) throws IOException; /** * Triggered to pass incoming data to the data consumer. The consumer must * consume the entire content of the data buffer. The consumer must stop * incrementing its capacity on the capacity channel if it is unable to * accept more data. Once the data consumer has handled accumulated data * or allocated more intermediate storage it can update its capacity * information on the capacity channel. * * @param src data source. */ void consume(ByteBuffer src) throws IOException; /** * Triggered to signal termination of the data stream. * * @param trailers data stream trailers. */ void streamEnd(List trailers) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerExchangeHandler.java0100664 0000000 0000000 00000004502 14245617503 027423 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract asynchronous server side message exchange handler that acts as a request consumer * and a response producer. * * @since 5.0 */ public interface AsyncServerExchangeHandler extends AsyncDataExchangeHandler { /** * Processes the actual HTTP request. The handler can choose to send * response messages immediately inside the call or asynchronously * at some later point. * * @param request the actual request. * @param entityDetails the request entity details or {@code null} if the request * does not enclose an entity. * @param responseChannel the response channel. * @param context the actual execution context. */ void handleRequest( HttpRequest request, EntityDetails entityDetails, ResponseChannel responseChannel, HttpContext context) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/NHttpMessageWriterFactory.java0100664 0000000 0000000 00000002666 14245617503 027316 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import org.apache.hc.core5.http.MessageHeaders; /** * Factory for {@link NHttpMessageWriter} instances. * * @since 4.3 */ public interface NHttpMessageWriterFactory { NHttpMessageWriter create(); } httpcore5/src/main/java/org/apache/hc/core5/http/nio/ResponseChannel.java0100664 0000000 0000000 00000005761 14245617503 025315 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract response / response promise channel. *

* Implementations are expected to be thread-safe. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public interface ResponseChannel { /** * Sends an intermediate informational HTTP response through this channel. * * @param response an intermediate (1xx) HTTP response. * @param context the actual execution context. */ void sendInformation(HttpResponse response, HttpContext context) throws HttpException, IOException; /** * Sends a final HTTP response through this channel. * * @param response a final (non 1xx) HTTP response * @param entityDetails the response entity details or {@code null} if the response * does not enclose an entity. * @param context the actual execution context. */ void sendResponse(HttpResponse response, EntityDetails entityDetails, HttpContext context) throws HttpException, IOException; /** * Pushes a request message head through this channel as a promise to deliver * a response message. * * @param promise the request message header used as a promise. * @param responseProducer the push response message producer. * @param context the actual execution context. */ void pushPromise(HttpRequest promise, AsyncPushProducer responseProducer, HttpContext context) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncRequestProducer.java0100664 0000000 0000000 00000004461 14245617503 026354 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract asynchronous request producer. * * @since 5.0 */ public interface AsyncRequestProducer extends AsyncDataProducer { /** * Triggered to signal the ability of the underlying request channel * to accept a request messages. The data producer can choose to send * a request message immediately inside the call or asynchronously * at some later point. * * @param channel the request channel capable to accepting a request message. * @param context the actual execution context. */ void sendRequest(RequestChannel channel, HttpContext context) throws HttpException, IOException; /** * Determines whether the producer can consistently produce the same content * after invocation of {@link ResourceHolder#releaseResources()}. */ boolean isRepeatable(); /** * Triggered to signal a failure in data generation. * * @param cause the cause of the failure. */ void failed(Exception cause); } httpcore5/src/main/java/org/apache/hc/core5/http/ConnectionRequestTimeoutException.java0100664 0000000 0000000 00000003603 14245617503 030330 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.InterruptedIOException; /** * Signals timeout requesting connection. * * @since 5.0 */ public class ConnectionRequestTimeoutException extends InterruptedIOException { private static final long serialVersionUID = 1L; /** * Creates a {@link ConnectionRequestTimeoutException} without details. */ public ConnectionRequestTimeoutException() { super(); } /** * Creates a {@link ConnectionRequestTimeoutException} with a detail message. * * @param message the exception detail message, or {@code null} */ public ConnectionRequestTimeoutException(final String message) { super(HttpException.clean(message)); } } httpcore5/src/main/java/org/apache/hc/core5/http/HttpResponse.java0100664 0000000 0000000 00000005077 14245617503 024077 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.util.Locale; /** * After receiving and interpreting a request message, a server responds * with an HTTP response message. * * @since 4.0 */ public interface HttpResponse extends HttpMessage { /** * Obtains the code of this response message. * * @return the status code. */ int getCode(); /** * Updates status code of this response message. * * @param code the HTTP status code. * * @see HttpStatus */ void setCode(int code); /** * Obtains the reason phrase of this response if available. * * @return the reason phrase. */ String getReasonPhrase(); /** * Updates the status line of this response with a new reason phrase. * * @param reason the new reason phrase as a single-line string, or * {@code null} to unset the reason phrase */ void setReasonPhrase(String reason); /** * Obtains the locale of this response. * The locale is used to determine the reason phrase * for the {@link #setCode status code}. * It can be changed using {@link #setLocale setLocale}. * * @return the locale of this response, never {@code null} */ Locale getLocale(); /** * Changes the locale of this response. * * @param loc the new locale */ void setLocale(Locale loc); } httpcore5/src/main/java/org/apache/hc/core5/http/HttpHost.java0100664 0000000 0000000 00000030112 14403631147 023176 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.Serializable; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; import java.util.Objects; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.net.Host; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.LangUtils; import org.apache.hc.core5.util.TextUtils; /** * Component that holds all details needed to describe an HTTP connection * to a host. This includes remote host name, port and protocol scheme. * * @see org.apache.hc.core5.net.Host * * @since 4.0 * @since 5.0 For constructors that take a scheme as an argument, that argument is now the first one. */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class HttpHost implements NamedEndpoint, Serializable { private static final long serialVersionUID = -7529410654042457626L; /** The default scheme is "http". */ public static final URIScheme DEFAULT_SCHEME = URIScheme.HTTP; private final String schemeName; private final Host host; private final InetAddress address; /** * Creates a new {@link HttpHost HttpHost}, specifying all values. * Constructor for HttpHost. * @param scheme the name of the scheme. * {@code null} indicates the * {@link #DEFAULT_SCHEME default scheme} * @param address the inet address. Can be {@code null} * @param hostname the hostname (IP or DNS name) * @param port the port number. * {@code -1} indicates the scheme default port. * * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. {@code -1} indicates the scheme default port. * * @since 5.0 */ public HttpHost(final String scheme, final InetAddress address, final String hostname, final int port) { Args.containsNoBlanks(hostname, "Host name"); this.host = new Host(hostname, port); this.schemeName = scheme != null ? TextUtils.toLowerCase(scheme) : DEFAULT_SCHEME.id; this.address = address; } /** * Creates {@code HttpHost} instance with the given scheme, hostname and port. * @param scheme the name of the scheme. * {@code null} indicates the * {@link #DEFAULT_SCHEME default scheme} * @param hostname the hostname (IP or DNS name) * @param port the port number. * {@code -1} indicates the scheme default port. * * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. {@code -1} indicates the scheme default port. */ public HttpHost(final String scheme, final String hostname, final int port) { this(scheme, null, hostname, port); } /** * Creates {@code HttpHost} instance with the default scheme and the given hostname and port. * * @param hostname the hostname (IP or DNS name) * @param port the port number. * {@code -1} indicates the scheme default port. * * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. {@code -1} indicates the scheme default port. */ public HttpHost(final String hostname, final int port) { this(null, hostname, port); } /** * Creates {@code HttpHost} instance with the given hostname and scheme and the default port for that scheme. * @param scheme the name of the scheme. * {@code null} indicates the * {@link #DEFAULT_SCHEME default scheme} * @param hostname the hostname (IP or DNS name) * * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. {@code -1} indicates the scheme default port. */ public HttpHost(final String scheme, final String hostname) { this(scheme, hostname, -1); } /** * Creates {@code HttpHost} instance from a string. Text may not contain any blanks. * * @since 4.4 */ public static HttpHost create(final String s) throws URISyntaxException { Args.notEmpty(s, "HTTP Host"); String text = s; String scheme = null; final int schemeIdx = text.indexOf("://"); if (schemeIdx > 0) { scheme = text.substring(0, schemeIdx); if (TextUtils.containsBlanks(scheme)) { throw new URISyntaxException(s, "scheme contains blanks"); } text = text.substring(schemeIdx + 3); } final Host host = Host.create(text); return new HttpHost(scheme, host); } /** * Creates an {@code HttpHost} instance from the scheme, host, and port from the given URI. Other URI elements are ignored. * * @param uri scheme, host, and port. * @return a new HttpHost * * @since 5.0 */ public static HttpHost create(final URI uri) { final String scheme = uri.getScheme(); return new HttpHost(scheme != null ? scheme : URIScheme.HTTP.getId(), uri.getHost(), uri.getPort()); } /** * Creates {@code HttpHost} instance with the default scheme and port and the given hostname. * * @param hostname the hostname (IP or DNS name) * * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. {@code -1} indicates the scheme default port. */ public HttpHost(final String hostname) { this(null, hostname, -1); } /** * Creates {@code HttpHost} instance with the given scheme, inet address and port. * @param scheme the name of the scheme. * {@code null} indicates the * {@link #DEFAULT_SCHEME default scheme} * @param address the inet address. * @param port the port number. * {@code -1} indicates the scheme default port. * * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. {@code -1} indicates the scheme default port. * * @since 5.0 */ public HttpHost(final String scheme, final InetAddress address, final int port) { this(scheme, Args.notNull(address,"Inet address"), address.getHostName(), port); } /** * Creates {@code HttpHost} instance with the default scheme and the given inet address * and port. * * @param address the inet address. * @param port the port number. * {@code -1} indicates the scheme default port. * * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. {@code -1} indicates the scheme default port. * * @since 4.3 */ public HttpHost(final InetAddress address, final int port) { this(null, address, port); } /** * Creates {@code HttpHost} instance with the default scheme and port and the given inet * address. * * @param address the inet address. * * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. {@code -1} indicates the scheme default port. * * @since 4.3 */ public HttpHost(final InetAddress address) { this(null, address, -1); } /** * @throws IllegalArgumentException * If the port parameter is outside the specified range of valid port values, which is between 0 and * 65535, inclusive. {@code -1} indicates the scheme default port. * * @since 5.0 */ public HttpHost(final String scheme, final NamedEndpoint namedEndpoint) { this(scheme, Args.notNull(namedEndpoint, "Named endpoint").getHostName(), namedEndpoint.getPort()); } /** * @since 5.0 * * @deprecated Use {@link HttpHost#HttpHost(String, NamedEndpoint)} */ @Deprecated public HttpHost(final URIAuthority authority) { this(null, authority); } /** * Returns the host name. * * @return the host name (IP or DNS name) */ @Override public String getHostName() { return this.host.getHostName(); } /** * Returns the port. * * @return the host port, or {@code -1} if not set */ @Override public int getPort() { return this.host.getPort(); } /** * Returns the scheme name. * * @return the scheme name */ public String getSchemeName() { return this.schemeName; } /** * Returns the inet address if explicitly set by a constructor, * {@code null} otherwise. * @return the inet address * * @since 4.3 */ public InetAddress getAddress() { return this.address; } /** * Return the host URI, as a string. * * @return the host URI */ public String toURI() { final StringBuilder buffer = new StringBuilder(); buffer.append(this.schemeName); buffer.append("://"); buffer.append(this.host.toString()); return buffer.toString(); } /** * Obtains the host string, without scheme prefix. * * @return the host string, for example {@code localhost:8080} */ public String toHostString() { return this.host.toString(); } @Override public String toString() { return toURI(); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof HttpHost) { final HttpHost that = (HttpHost) obj; return this.schemeName.equals(that.schemeName) && this.host.equals(that.host) && Objects.equals(this.address, that.address); } return false; } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, this.schemeName); hash = LangUtils.hashCode(hash, this.host); hash = LangUtils.hashCode(hash, this.address); return hash; } } httpcore5/src/main/java/org/apache/hc/core5/http/HttpConnection.java0100664 0000000 0000000 00000006162 14245617503 024374 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; import java.net.SocketAddress; import javax.net.ssl.SSLSession; /** * A generic HTTP connection, useful on client and server side. * * @since 4.0 */ public interface HttpConnection extends SocketModalCloseable { /** * Closes this connection gracefully. This method will attempt to flush the internal output * buffer prior to closing the underlying socket. This method MUST NOT be called from a * different thread to force shutdown of the connection. Use {@link #close shutdown} instead. */ @Override void close() throws IOException; /** * Returns this connection's endpoint details. * * @return this connection's endpoint details. */ EndpointDetails getEndpointDetails(); /** * Returns this connection's local address or {@code null} if it is not bound yet. * * @return this connection's local address or {@code null} if it is not bound yet. * @since 5.0 */ SocketAddress getLocalAddress(); /** * Returns this connection's remote address or {@code null} if it is not connected yet or * unconnected. * * @return this connection's remote address or {@code null} if it is not connected yet or * unconnected. * @since 5.0 */ SocketAddress getRemoteAddress(); /** * Returns this connection's protocol version or {@code null} if unknown. * * @return this connection's protocol version or {@code null} if unknown. * @since 5.0 */ ProtocolVersion getProtocolVersion(); /** * Returns this connection's SSL session or {@code null} if TLS has not been activated. * * @return this connection's SSL session or {@code null} if TLS has not been activated. */ SSLSession getSSLSession(); /** * Checks if this connection is open. * * @return true if it is open, false if it is closed. */ boolean isOpen(); } httpcore5/src/main/java/org/apache/hc/core5/http/NoHttpResponseException.java0100664 0000000 0000000 00000004062 14245617503 026244 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; /** * Signals that the target server failed to respond with a valid HTTP response. * * @since 4.0 */ public class NoHttpResponseException extends IOException { private static final long serialVersionUID = -7658940387386078766L; /** * Creates a new NoHttpResponseException with the specified detail message. * * @param message exception message */ public NoHttpResponseException(final String message) { super(HttpException.clean(message)); } /** * Constructs a {@code NoHttpResponseException} with the specified detail message * and cause. * * @param message The exception detail message * @param cause The cause. * * @since 5.0 */ public NoHttpResponseException(final String message, final Throwable cause) { super(message, cause); } } httpcore5/src/main/java/org/apache/hc/core5/http/MessageHeaders.java0100664 0000000 0000000 00000011033 14245617503 024306 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.util.Iterator; /** * Messages head consisting of multiple message headers. * * @since 5.0 */ public interface MessageHeaders { /** * Checks if a certain header is present in this message. Header values are * ignored. * * @param name the header name to check for. * @return true if at least one header with this name is present. */ boolean containsHeader(String name); /** * Checks if a certain header is present in this message and how many times. * * @param name the header name to check for. * @return number of occurrences of the header in the message. */ int countHeaders(String name); /** * Returns the first header with a specified name of this message. Header * values are ignored. If there is more than one matching header in the * message the first element of {@link #getHeaders(String)} is returned. * If there is no matching header in the message {@code null} is * returned. * * @param name the name of the header to return. * @return the first header whose name property equals {@code name} * or {@code null} if no such header could be found. */ Header getFirstHeader(String name); /** * Gets single first header with the given name. * *

Header name comparison is case insensitive. * * @param name the name of the header to get * @return the first header or {@code null} * @throws ProtocolException in case multiple headers with the given name are found. */ Header getHeader(String name) throws ProtocolException; /** * Returns all the headers of this message. Headers are ordered in the sequence * they will be sent over a connection. * * @return all the headers of this message */ Header[] getHeaders(); /** * Returns all the headers with a specified name of this message. Header values * are ignored. Headers are ordered in the sequence they will be sent over a * connection. * * @param name the name of the headers to return. * @return the headers whose name property equals {@code name}. */ Header[] getHeaders(String name); /** * Returns the last header with a specified name of this message. Header values * are ignored. If there is more than one matching header in the message the * last element of {@link #getHeaders(String)} is returned. If there is no * matching header in the message {@code null} is returned. * * @param name the name of the header to return. * @return the last header whose name property equals {@code name}. * or {@code null} if no such header could be found. */ Header getLastHeader(String name); /** * Returns an iterator of all the headers. * * @return Iterator that returns Header objects in the sequence they are * sent over a connection. */ Iterator

headerIterator(); /** * Returns an iterator of the headers with a given name. * * @param name the name of the headers over which to iterate, or * {@code null} for all headers * * @return Iterator that returns Header objects with the argument name * in the sequence they are sent over a connection. */ Iterator
headerIterator(String name); } httpcore5/src/main/java/org/apache/hc/core5/http/TruncatedChunkException.java0100664 0000000 0000000 00000004163 14245617503 026235 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Signals a truncated chunk in a chunked stream. * * @since 4.1 */ public class TruncatedChunkException extends MalformedChunkCodingException { private static final long serialVersionUID = -23506263930279460L; /** * Creates a TruncatedChunkException with the specified detail message. * * @param message The exception detail message */ public TruncatedChunkException(final String message) { super(HttpException.clean(message)); } /** * Constructs a new TruncatedChunkException with the specified detail message. * * @param format The exception detail message format; see {@link String#format(String, Object...)}. * @param args The exception detail message arguments; see {@link String#format(String, Object...)}. * * @since 5.0 */ public TruncatedChunkException(final String format, final Object... args) { super(format, args); } } httpcore5/src/main/java/org/apache/hc/core5/http/Header.java0100664 0000000 0000000 00000003321 14245617503 022617 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Represents an HTTP header field consisting of a field name and a field * value. * * @since 4.0 */ public interface Header extends NameValuePair { /** * Returns {@code true} if the header should be considered sensitive. *

* Some encoding schemes such as HPACK impose restrictions on encoded * representation of sensitive headers. *

* * @return {@code true} if the header should be considered sensitive. * * @since 5.0 */ boolean isSensitive(); } httpcore5/src/main/java/org/apache/hc/core5/http/UnsupportedHttpVersionException.java0100664 0000000 0000000 00000004136 14245617503 030051 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Signals an unsupported version of the HTTP protocol. * * @since 4.0 */ public class UnsupportedHttpVersionException extends ProtocolException { private static final long serialVersionUID = -1348448090193107031L; /** * Creates an exception without a detail message. */ public UnsupportedHttpVersionException() { super(); } /** * Creates an exception with a detail message for the given ProtocolVersion. * * @param protocolVersion The unsupported ProtocolVersion. */ public UnsupportedHttpVersionException(final ProtocolVersion protocolVersion) { super("Unsupported version: " + protocolVersion); } /** * Creates an exception with the specified detail message. * * @param message The exception detail message */ public UnsupportedHttpVersionException(final String message) { super(message); } } httpcore5/src/main/java/org/apache/hc/core5/http/HttpStreamResetException.java0100664 0000000 0000000 00000003226 14245617503 026410 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; /** * Signals HTTP protocol error that renders the actual HTTP data stream unreliable. * * @since 5.0 */ public class HttpStreamResetException extends IOException { private static final long serialVersionUID = 1L; public HttpStreamResetException(final String message) { super(message); } public HttpStreamResetException(final String message, final Throwable cause) { super(message, cause); } } httpcore5/src/main/java/org/apache/hc/core5/http/ParseException.java0100664 0000000 0000000 00000005343 14245617503 024366 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Signals a protocol exception due to failure to parse a message element. * * @since 4.0 */ public class ParseException extends ProtocolException { private static final long serialVersionUID = -7288819855864183578L; private final int errorOffset; /** * Creates a {@link ParseException} without details. */ public ParseException() { super(); this.errorOffset = -1; } /** * Creates a {@link ParseException} with a detail message. * * @param message the exception detail message, or {@code null} */ public ParseException(final String message) { super(message); this.errorOffset = -1; } /** * Creates a {@link ParseException} with parsing context details. * * @since 5.0 */ public ParseException(final String description, final CharSequence text, final int off, final int len, final int errorOffset) { super(description + (errorOffset >= 0 ? "; error at offset " + errorOffset : "") + (text != null && len < 1024 ? ": <" + text.subSequence(off, off + len) + ">" : "")); this.errorOffset = errorOffset; } /** * Creates a {@link ParseException} with parsing context details. * * @since 5.0 */ public ParseException(final String description, final CharSequence text, final int off, final int len) { this(description, text, off, len, -1); } /** * @since 5.0 */ public int getErrorOffset() { return errorOffset; } } httpcore5/src/main/java/org/apache/hc/core5/http/ssl/0040775 0000000 0000000 00000000000 14403631147 021365 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TLS.java0100664 0000000 0000000 00000007573 14403631147 022703 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.ssl; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Tokenizer; /** * Supported {@code TLS} protocol versions. * * @since 5.0 */ public enum TLS { V_1_0("TLSv1", new ProtocolVersion("TLS", 1, 0)), V_1_1("TLSv1.1", new ProtocolVersion("TLS", 1, 1)), V_1_2("TLSv1.2", new ProtocolVersion("TLS", 1, 2)), V_1_3("TLSv1.3", new ProtocolVersion("TLS", 1, 3)); public final String id; public final ProtocolVersion version; TLS(final String id, final ProtocolVersion version) { this.id = id; this.version = version; } public boolean isSame(final ProtocolVersion protocolVersion) { return version.equals(protocolVersion); } public boolean isComparable(final ProtocolVersion protocolVersion) { return version.isComparable(protocolVersion); } /** * Gets the ID. * @return the ID. * * @since 5.2 */ public String getId() { return id; } /** * Gets the version. * @return the version. * * @since 5.2 */ public ProtocolVersion getVersion() { return version; } public boolean greaterEquals(final ProtocolVersion protocolVersion) { return version.greaterEquals(protocolVersion); } public boolean lessEquals(final ProtocolVersion protocolVersion) { return version.lessEquals(protocolVersion); } public static ProtocolVersion parse(final String s) throws ParseException { if (s == null) { return null; } final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); return TlsVersionParser.INSTANCE.parse(s, cursor, null); } public static String[] excludeWeak(final String... protocols) { if (protocols == null) { return null; } final List enabledProtocols = new ArrayList<>(); for (final String protocol : protocols) { if (isSecure(protocol)) { enabledProtocols.add(protocol); } } if (enabledProtocols.isEmpty()) { enabledProtocols.add(V_1_2.id); } return enabledProtocols.toArray(new String[0]); } /** * Check if a given protocol is considered secure and is enabled by default. * * @return {@code true} if the given protocol is secure and enabled, otherwise return {@code * false}. * @since 5.2 */ public static boolean isSecure(final String protocol) { return !protocol.startsWith("SSL") && !protocol.equals(V_1_0.id) && !protocol.equals(V_1_1.id); } } httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsVersionParser.java0100664 0000000 0000000 00000007665 14245617503 025534 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.ssl; import java.util.BitSet; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Tokenizer; final class TlsVersionParser { public final static TlsVersionParser INSTANCE = new TlsVersionParser(); private final Tokenizer tokenizer; TlsVersionParser() { this.tokenizer = Tokenizer.INSTANCE; } ProtocolVersion parse( final CharSequence buffer, final Tokenizer.Cursor cursor, final BitSet delimiters) throws ParseException { final int lowerBound = cursor.getLowerBound(); final int upperBound = cursor.getUpperBound(); int pos = cursor.getPos(); if (pos + 4 > cursor.getUpperBound()) { throw new ParseException("Invalid TLS protocol version", buffer, lowerBound, upperBound, pos); } if (buffer.charAt(pos) != 'T' || buffer.charAt(pos + 1) != 'L' || buffer.charAt(pos + 2) != 'S' || buffer.charAt(pos + 3) != 'v') { throw new ParseException("Invalid TLS protocol version", buffer, lowerBound, upperBound, pos); } pos = pos + 4; cursor.updatePos(pos); if (cursor.atEnd()) { throw new ParseException("Invalid TLS version", buffer, lowerBound, upperBound, pos); } final String s = this.tokenizer.parseToken(buffer, cursor, delimiters); final int idx = s.indexOf('.'); if (idx == -1) { final int major; try { major = Integer.parseInt(s); } catch (final NumberFormatException e) { throw new ParseException("Invalid TLS major version", buffer, lowerBound, upperBound, pos); } return new ProtocolVersion("TLS", major, 0); } else { final String s1 = s.substring(0, idx); final int major; try { major = Integer.parseInt(s1); } catch (final NumberFormatException e) { throw new ParseException("Invalid TLS major version", buffer, lowerBound, upperBound, pos); } final String s2 = s.substring(idx + 1); final int minor; try { minor = Integer.parseInt(s2); } catch (final NumberFormatException e) { throw new ParseException("Invalid TLS minor version", buffer, lowerBound, upperBound, pos); } return new ProtocolVersion("TLS", major, minor); } } ProtocolVersion parse(final String s) throws ParseException { if (s == null) { return null; } final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); return parse(s, cursor, null); } } httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java0100664 0000000 0000000 00000046101 14315123013 024275 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.ssl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; /** * TLS cipher suite support methods * * @since 5.0 */ public final class TlsCiphers { private final static Set H2_BLACKLISTED = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( "TLS_NULL_WITH_NULL_NULL", "TLS_RSA_WITH_NULL_MD5", "TLS_RSA_WITH_NULL_SHA", "TLS_RSA_EXPORT_WITH_RC4_40_MD5", "TLS_RSA_WITH_RC4_128_MD5", "TLS_RSA_WITH_RC4_128_SHA", "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5", "TLS_RSA_WITH_IDEA_CBC_SHA", "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", "TLS_RSA_WITH_DES_CBC_SHA", "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", "TLS_DH_DSS_WITH_DES_CBC_SHA", "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", "TLS_DH_RSA_WITH_DES_CBC_SHA", "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", "TLS_DHE_DSS_WITH_DES_CBC_SHA", "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "TLS_DHE_RSA_WITH_DES_CBC_SHA", "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", "TLS_DH_anon_WITH_RC4_128_MD5", "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", "TLS_DH_anon_WITH_DES_CBC_SHA", "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA", "TLS_KRB5_WITH_DES_CBC_SHA", "TLS_KRB5_WITH_3DES_EDE_CBC_SHA", "TLS_KRB5_WITH_RC4_128_SHA", "TLS_KRB5_WITH_IDEA_CBC_SHA", "TLS_KRB5_WITH_DES_CBC_MD5", "TLS_KRB5_WITH_3DES_EDE_CBC_MD5", "TLS_KRB5_WITH_RC4_128_MD5", "TLS_KRB5_WITH_IDEA_CBC_MD5", "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", "TLS_KRB5_EXPORT_WITH_RC4_40_MD5", "TLS_PSK_WITH_NULL_SHA", "TLS_DHE_PSK_WITH_NULL_SHA", "TLS_RSA_PSK_WITH_NULL_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_DH_DSS_WITH_AES_128_CBC_SHA", "TLS_DH_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DH_anon_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA", "TLS_DH_DSS_WITH_AES_256_CBC_SHA", "TLS_DH_RSA_WITH_AES_256_CBC_SHA", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "TLS_DH_anon_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_NULL_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_256_CBC_SHA256", "TLS_DH_DSS_WITH_AES_128_CBC_SHA256", "TLS_DH_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA", "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA", "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DH_DSS_WITH_AES_256_CBC_SHA256", "TLS_DH_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_DH_anon_WITH_AES_128_CBC_SHA256", "TLS_DH_anon_WITH_AES_256_CBC_SHA256", "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA", "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA", "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", "TLS_PSK_WITH_RC4_128_SHA", "TLS_PSK_WITH_3DES_EDE_CBC_SHA", "TLS_PSK_WITH_AES_128_CBC_SHA", "TLS_PSK_WITH_AES_256_CBC_SHA", "TLS_DHE_PSK_WITH_RC4_128_SHA", "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA", "TLS_DHE_PSK_WITH_AES_128_CBC_SHA", "TLS_DHE_PSK_WITH_AES_256_CBC_SHA", "TLS_RSA_PSK_WITH_RC4_128_SHA", "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA", "TLS_RSA_PSK_WITH_AES_128_CBC_SHA", "TLS_RSA_PSK_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_SEED_CBC_SHA", "TLS_DH_DSS_WITH_SEED_CBC_SHA", "TLS_DH_RSA_WITH_SEED_CBC_SHA", "TLS_DHE_DSS_WITH_SEED_CBC_SHA", "TLS_DHE_RSA_WITH_SEED_CBC_SHA", "TLS_DH_anon_WITH_SEED_CBC_SHA", "TLS_RSA_WITH_AES_128_GCM_SHA256", "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_DH_RSA_WITH_AES_128_GCM_SHA256", "TLS_DH_RSA_WITH_AES_256_GCM_SHA384", "TLS_DH_DSS_WITH_AES_128_GCM_SHA256", "TLS_DH_DSS_WITH_AES_256_GCM_SHA384", "TLS_DH_anon_WITH_AES_128_GCM_SHA256", "TLS_DH_anon_WITH_AES_256_GCM_SHA384", "TLS_PSK_WITH_AES_128_GCM_SHA256", "TLS_PSK_WITH_AES_256_GCM_SHA384", "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256", "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384", "TLS_PSK_WITH_AES_128_CBC_SHA256", "TLS_PSK_WITH_AES_256_CBC_SHA384", "TLS_PSK_WITH_NULL_SHA256", "TLS_PSK_WITH_NULL_SHA384", "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256", "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384", "TLS_DHE_PSK_WITH_NULL_SHA256", "TLS_DHE_PSK_WITH_NULL_SHA384", "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256", "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384", "TLS_RSA_PSK_WITH_NULL_SHA256", "TLS_RSA_PSK_WITH_NULL_SHA384", "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256", "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256", "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256", "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256", "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256", "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", "TLS_ECDH_ECDSA_WITH_NULL_SHA", "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_NULL_SHA", "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDH_RSA_WITH_NULL_SHA", "TLS_ECDH_RSA_WITH_RC4_128_SHA", "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_NULL_SHA", "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDH_anon_WITH_NULL_SHA", "TLS_ECDH_anon_WITH_RC4_128_SHA", "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA", "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", "TLS_SRP_SHA_WITH_AES_128_CBC_SHA", "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA", "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA", "TLS_SRP_SHA_WITH_AES_256_CBC_SHA", "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA", "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_PSK_WITH_RC4_128_SHA", "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_PSK_WITH_NULL_SHA", "TLS_ECDHE_PSK_WITH_NULL_SHA256", "TLS_ECDHE_PSK_WITH_NULL_SHA384", "TLS_RSA_WITH_ARIA_128_CBC_SHA256", "TLS_RSA_WITH_ARIA_256_CBC_SHA384", "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256", "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384", "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256", "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384", "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256", "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384", "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256", "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384", "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256", "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256", "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256", "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384", "TLS_RSA_WITH_ARIA_128_GCM_SHA256", "TLS_RSA_WITH_ARIA_256_GCM_SHA384", "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256", "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384", "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256", "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384", "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256", "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384", "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256", "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384", "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256", "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384", "TLS_PSK_WITH_ARIA_128_CBC_SHA256", "TLS_PSK_WITH_ARIA_256_CBC_SHA384", "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256", "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384", "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256", "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384", "TLS_PSK_WITH_ARIA_128_GCM_SHA256", "TLS_PSK_WITH_ARIA_256_GCM_SHA384", "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256", "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384", "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256", "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256", "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384", "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256", "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384", "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256", "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384", "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256", "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384", "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256", "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384", "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384", "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256", "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384", "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256", "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384", "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256", "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384", "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256", "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384", "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", "TLS_RSA_WITH_AES_128_CCM", "TLS_RSA_WITH_AES_256_CCM", "TLS_RSA_WITH_AES_128_CCM_8", "TLS_RSA_WITH_AES_256_CCM_8", "TLS_PSK_WITH_AES_128_CCM", "TLS_PSK_WITH_AES_256_CCM", "TLS_PSK_WITH_AES_128_CCM_8", "TLS_PSK_WITH_AES_256_CCM_8" ))); public static boolean isH2Blacklisted(final String cipherSuite) { return H2_BLACKLISTED.contains(cipherSuite); } private static final String WEAK_KEY_EXCHANGES = "^(TLS|SSL)_(NULL|ECDH_anon|DH_anon|DH_anon_EXPORT|DHE_RSA_EXPORT|DHE_DSS_EXPORT|" + "DSS_EXPORT|DH_DSS_EXPORT|DH_RSA_EXPORT|RSA_EXPORT|KRB5_EXPORT)_(.*)"; private static final String WEAK_CIPHERS = "^(TLS|SSL)_(.*)_WITH_(NULL|DES_CBC|DES40_CBC|DES_CBC_40|3DES_EDE_CBC|RC4_128|RC4_40|RC2_CBC_40)_(.*)"; private static final List WEAK_CIPHER_SUITE_PATTERNS = Collections.unmodifiableList(Arrays.asList( Pattern.compile(WEAK_KEY_EXCHANGES, Pattern.CASE_INSENSITIVE), Pattern.compile(WEAK_CIPHERS, Pattern.CASE_INSENSITIVE))); public static boolean isWeak(final String cipherSuite) { for (final Pattern pattern : WEAK_CIPHER_SUITE_PATTERNS) { if (pattern.matcher(cipherSuite).matches()) { return true; } } return false; } public static String[] excludeH2Blacklisted(final String... ciphers) { if (ciphers == null) { return null; } final List enabledCiphers = new ArrayList<>(); for (final String cipher: ciphers) { if (!TlsCiphers.isH2Blacklisted(cipher)) { enabledCiphers.add(cipher); } } return !enabledCiphers.isEmpty() ? enabledCiphers.toArray(new String[0]) : ciphers; } public static String[] excludeWeak(final String... ciphers) { if (ciphers == null) { return null; } final List enabledCiphers = new ArrayList<>(); for (final String cipher: ciphers) { if (!TlsCiphers.isWeak(cipher)) { enabledCiphers.add(cipher); } } return !enabledCiphers.isEmpty() ? enabledCiphers.toArray(new String[0]) : ciphers; } } httpcore5/src/main/java/org/apache/hc/core5/http/ContentLengthStrategy.java0100664 0000000 0000000 00000004427 14245617503 025736 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Represents a strategy to determine length of the enclosed content entity * based on properties of the HTTP message. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface ContentLengthStrategy { /** * Message body chunk coded */ long CHUNKED = -1; /** * Message body not explicitly delineated. Legal for HTTP response messages * and illegal for HTTP request messages. */ long UNDEFINED = -Long.MAX_VALUE; /** * Returns length of the given message in bytes. The returned value * must be a non-negative number, {@link #CHUNKED} if the message is * chunk coded, or {@link #UNDEFINED} if the message is not explicitly * delineated. * * @param message HTTP message * @return content length, {@link #UNDEFINED}, or {@link #CHUNKED} * * @throws HttpException in case of HTTP protocol violation */ long determineLength(HttpMessage message) throws HttpException; } httpcore5/src/main/java/org/apache/hc/core5/http/MessageConstraintException.java0100664 0000000 0000000 00000003272 14245617503 026744 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; /** * Signals a message constraint violation. * * @since 4.3 */ public class MessageConstraintException extends IOException { private static final long serialVersionUID = 6077207720446368695L; /** * Creates a MessageConstraintException with the specified detail message. * * @param message The exception detail message */ public MessageConstraintException(final String message) { super(HttpException.clean(message)); } } httpcore5/src/main/java/org/apache/hc/core5/http/MethodNotSupportedException.java0100664 0000000 0000000 00000004111 14245617503 027113 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Signals that an HTTP method is not supported. * * @since 4.0 */ public class MethodNotSupportedException extends ProtocolException { private static final long serialVersionUID = 1L; /** * Creates a new MethodNotSupportedException with the specified detail message. * * @param message The exception detail message */ public MethodNotSupportedException(final String message) { super(message); } /** * Creates a new MethodNotSupportedException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public MethodNotSupportedException(final String message, final Throwable cause) { super(message, cause); } } httpcore5/src/main/java/org/apache/hc/core5/http/EntityDetails.java0100664 0000000 0000000 00000003711 14245617503 024214 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.util.Set; /** * Details of an entity transmitted by a message. * * @since 5.0 */ public interface EntityDetails { /** * Returns length of the entity, if known. */ long getContentLength(); /** * Returns content type of the entity, if known. */ String getContentType(); /** * Returns content encoding of the entity, if known. */ String getContentEncoding(); /** * Returns chunked transfer hint for this entity. *

* The behavior of wrapping entities is implementation dependent, * but should respect the primary purpose. *

*/ boolean isChunked(); /** * Preliminary declaration of trailing headers. */ Set getTrailerNames(); } httpcore5/src/main/java/org/apache/hc/core5/http/ConnectionReuseStrategy.java0100664 0000000 0000000 00000006105 14245617503 026260 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * Interface for deciding whether a connection can be re-used for * subsequent requests and should be kept alive. *

* Implementations of this interface must be thread-safe. Access to shared * data must be synchronized as methods of this interface may be executed * from multiple threads. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface ConnectionReuseStrategy { /** * Decides whether a connection can be kept open after a request. * If this method returns {@code false}, the caller MUST * close the connection to correctly comply with the HTTP protocol. * If it returns {@code true}, the caller SHOULD attempt to * keep the connection open for reuse with another request. *

* One can use the HTTP context to retrieve additional objects that * may be relevant for the keep-alive strategy: the actual HTTP * connection, the original HTTP request, target host if known, * number of times the connection has been reused already and so on. *

*

* If the connection is already closed, {@code false} is returned. * The stale connection check MUST NOT be triggered by a * connection reuse strategy. *

* * @param request * The last request transmitted over that connection. * @param response * The last response transmitted over that connection. * @param context the context in which the connection is being * used. * * @return {@code true} if the connection is allowed to be reused, or * {@code false} if it MUST NOT be reused */ boolean keepAlive(HttpRequest request, HttpResponse response, HttpContext context); } httpcore5/src/main/java/org/apache/hc/core5/http/HttpEntityContainer.java0100664 0000000 0000000 00000003526 14245617503 025415 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Contains an {@link HttpEntity}. * * @since 5.0 */ public interface HttpEntityContainer { /** * Obtains the message entity, if available. * * @return the message entity, or {@code null} if not available */ HttpEntity getEntity(); /** * Sets an entity for this message. *

* Please note that if an entity has already been set it is responsibility of the caller * to ensure release of the resources that may be associated with that entity. *

* * @param entity the entity to set of this message, or {@code null} to unset */ void setEntity(HttpEntity entity); } httpcore5/src/main/java/org/apache/hc/core5/http/LengthRequiredException.java0100664 0000000 0000000 00000003446 14245617503 026240 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Signals message rejection due to missing content length. * * @since 4.0 */ public class LengthRequiredException extends ProtocolException { private static final long serialVersionUID = 1049109801075840707L; /** * Creates an exception without a default detail message. */ public LengthRequiredException() { super("Length required"); } /** * Creates an exception with the specified detail message. * * @param message The exception detail message */ public LengthRequiredException(final String message) { super(message); } } httpcore5/src/main/java/org/apache/hc/core5/http/HeaderElement.java0100664 0000000 0000000 00000004431 14245617503 024134 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Represents an element of an HTTP {@link Header header} value consisting of * a name / value pair and a number of optional name / value parameters. * * @since 4.0 */ public interface HeaderElement { /** * Returns header element name. * * @return header element name */ String getName(); /** * Returns header element value. * * @return header element value */ String getValue(); /** * Returns an array of name / value pairs. * * @return array of name / value pairs */ NameValuePair[] getParameters(); /** * Returns the first parameter with the given name. * * @param name parameter name * * @return name / value pair */ NameValuePair getParameterByName(String name); /** * Returns the total count of parameters. * * @return parameter count */ int getParameterCount(); /** * Returns parameter with the given index. * * @param index index * @return name / value pair */ NameValuePair getParameter(int index); } httpcore5/src/main/java/org/apache/hc/core5/http/HttpConnectionMetrics.java0100664 0000000 0000000 00000003604 14245617503 025721 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * The point of access to the statistics of an {@link HttpConnection}. * * @since 4.0 */ public interface HttpConnectionMetrics { /** * Gets the number of requests transferred over the connection, * 0 if not available. */ long getRequestCount(); /** * Gets the number of responses transferred over the connection, * 0 if not available. */ long getResponseCount(); /** * Gets the number of bytes transferred over the connection, * 0 if not available. */ long getSentBytesCount(); /** * Gets the number of bytes transferred over the connection, * 0 if not available. */ long getReceivedBytesCount(); } httpcore5/src/main/java/org/apache/hc/core5/http/FormattedHeader.java0100664 0000000 0000000 00000004150 14245617503 024466 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.apache.hc.core5.util.CharArrayBuffer; /** * An HTTP header which is already formatted. * For example when headers are received, the original formatting * can be preserved. This allows for the header to be sent without * another formatting step. * * @since 4.0 */ public interface FormattedHeader extends Header { /** * Obtains the buffer with the formatted header. * The returned buffer MUST NOT be modified. * * @return the formatted header, in a buffer that must not be modified */ CharArrayBuffer getBuffer(); /** * Obtains the start of the header value in the {@link #getBuffer buffer}. * By accessing the value in the buffer, creation of a temporary string * can be avoided. * * @return index of the first character of the header value * in the buffer returned by {@link #getBuffer getBuffer}. */ int getValuePos(); } httpcore5/src/main/java/org/apache/hc/core5/http/io/0040775 0000000 0000000 00000000000 14435411677 021204 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/io/package-info.java0100664 0000000 0000000 00000002425 14245617503 024366 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core HTTP transport APIs based on the classic (blocking) I/O model. */ package org.apache.hc.core5.http.io; httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpClientConnection.java0100664 0000000 0000000 00000010000 14403631147 026120 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; /** * A client-side HTTP connection, which can be used for sending * requests and receiving responses. * * @since 4.0 */ public interface HttpClientConnection extends BHttpConnection { /** * Checks whether this connection is in a consistent state. * * @return {@code true} if the connection is known to be * in a inconsistent state and cannot be re-used. * * @see #terminateRequest(ClassicHttpRequest) * * @since 5.0 */ boolean isConsistent(); /** * Sends the request line and all headers over the connection. * @param request the request whose headers to send. * @throws HttpException in case of HTTP protocol violation * @throws IOException in case of an I/O error */ void sendRequestHeader(ClassicHttpRequest request) throws HttpException, IOException; /** * Terminates request prematurely potentially leaving * the connection in a inconsistent state. * * @param request the request to be terminated prematurely. * @throws HttpException * @throws IOException * * @see #isConsistent() * * @since 5.0 */ void terminateRequest(ClassicHttpRequest request) throws HttpException, IOException; /** * Sends the request entity over the connection. * @param request the request whose entity to send. * @throws HttpException in case of HTTP protocol violation * @throws IOException in case of an I/O error */ void sendRequestEntity(ClassicHttpRequest request) throws HttpException, IOException; /** * Receives the request line and headers of the next response available from * this connection. The caller should examine the HttpResponse object to * find out if it should try to receive a response entity as well. * * @return a new HttpResponse object with status line and headers * initialized or {@code null} if the connection has been closed * by the opposite endpoint. * @throws HttpException in case of HTTP protocol violation * @throws IOException in case of an I/O error */ ClassicHttpResponse receiveResponseHeader() throws HttpException, IOException; /** * Receives the next response entity available from this connection and * attaches it to an existing HttpResponse object. * * @param response the response to attach the entity to * @throws HttpException in case of HTTP protocol violation * @throws IOException in case of an I/O error */ void receiveResponseEntity(ClassicHttpResponse response) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/SessionOutputBuffer.java0100664 0000000 0000000 00000010343 14245617503 026036 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import java.io.OutputStream; import org.apache.hc.core5.util.CharArrayBuffer; /** * Session output buffer for blocking HTTP/1.1 connections. *

* This interface facilitates intermediate buffering of output data streamed out * to an output stream and provides provides methods for writing lines of text. * * @since 4.0 */ public interface SessionOutputBuffer { /** * Return length data stored in the buffer * * @return data length */ int length(); /** * Returns total capacity of the buffer * * @return total capacity */ int capacity(); /** * Returns available space in the buffer. * * @return available space. */ int available(); /** * Writes {@code len} bytes from the specified byte array * starting at offset {@code off} to this session buffer. *

* If {@code off} is negative, or {@code len} is negative, or * {@code off+len} is greater than the length of the array * {@code b}, then an {@code IndexOutOfBoundsException} is thrown. * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @throws IOException if an I/O error occurs. */ void write(byte[] b, int off, int len, OutputStream outputStream) throws IOException; /** * Writes {@code b.length} bytes from the specified byte array * to this session buffer. * * @param b the data. * @throws IOException if an I/O error occurs. */ void write(byte[] b, OutputStream outputStream) throws IOException; /** * Writes the specified byte to this session buffer. * * @param b the {@code byte}. * @throws IOException if an I/O error occurs. */ void write(int b, OutputStream outputStream) throws IOException; /** * Writes characters from the specified char array followed by a line * delimiter to this session buffer. *

* The choice of a char encoding and line delimiter sequence is up to the * specific implementations of this interface. * * @param buffer the buffer containing chars of the line. * @throws IOException if an I/O error occurs. */ void writeLine(CharArrayBuffer buffer, OutputStream outputStream) throws IOException; /** * Flushes this session buffer and forces any buffered output bytes * to be written out. The general contract of {@code flush} is * that calling it is an indication that, if any bytes previously * written have been buffered by the implementation of the output * stream, such bytes should immediately be written to their * intended destination. * * @throws IOException if an I/O error occurs. */ void flush(OutputStream outputStream) throws IOException; /** * Returns {@link HttpTransportMetrics} for this session buffer. * * @return transport metrics. */ HttpTransportMetrics getMetrics(); } httpcore5/src/main/java/org/apache/hc/core5/http/io/EofSensorWatcher.java0100664 0000000 0000000 00000007244 14245617503 025267 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import java.io.InputStream; /** * A watcher for {@link EofSensorInputStream}. Each stream will notify its * watcher at most once. * * @since 4.0 */ public interface EofSensorWatcher { /** * Indicates that EOF is detected. * * @param wrapped the underlying stream which has reached EOF * * @return {@code true} if {@code wrapped} should be closed, * {@code false} if it should be left alone * * @throws IOException * in case of an IO problem, for example if the watcher itself * closes the underlying stream. The caller will leave the * wrapped stream alone, as if {@code false} was returned. */ boolean eofDetected(InputStream wrapped) throws IOException; /** * Indicates that the {@link EofSensorInputStream stream} is closed. * This method will be called only if EOF was not detected * before closing. Otherwise, {@link #eofDetected eofDetected} is called. * * @param wrapped the underlying stream which has not reached EOF * * @return {@code true} if {@code wrapped} should be closed, * {@code false} if it should be left alone * * @throws IOException * in case of an IO problem, for example if the watcher itself * closes the underlying stream. The caller will leave the * wrapped stream alone, as if {@code false} was returned. */ boolean streamClosed(InputStream wrapped) throws IOException; /** * Indicates that the {@link EofSensorInputStream stream} is aborted. * This method will be called only if EOF was not detected * before aborting. Otherwise, {@link #eofDetected eofDetected} is called. *

* This method will also be invoked when an input operation causes an * IOException to be thrown to make sure the input stream gets shut down. *

* * @param wrapped the underlying stream which has not reached EOF * * @return {@code true} if {@code wrapped} should be closed, * {@code false} if it should be left alone * * @throws IOException * in case of an IO problem, for example if the watcher itself * closes the underlying stream. The caller will leave the * wrapped stream alone, as if {@code false} was returned. */ boolean streamAbort(InputStream wrapped) throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpTransportMetrics.java0100664 0000000 0000000 00000002610 14245617503 026221 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; /** * Transport level metrics. * * @since 4.0 */ public interface HttpTransportMetrics { /** * Returns the number of bytes transferred. */ long getBytesTransferred(); } httpcore5/src/main/java/org/apache/hc/core5/http/io/ResponseOutOfOrderStrategy.java0100664 0000000 0000000 00000005622 14245617503 027336 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.ClassicHttpRequest; import java.io.IOException; import java.io.InputStream; /** * Represents a strategy to determine how frequently the client should check for an out of order response. * An out of order response is sent before the server has read the full request. If the client fails to * check for an early response then a {@link java.net.SocketException} or {@link java.net.SocketTimeoutException} * may be thrown while writing the request entity after a timeout is reached on either the client or server. * * @since 5.1 */ @Internal public interface ResponseOutOfOrderStrategy { /** * Called before each write to the to a socket {@link java.io.OutputStream} with the number of * bytes that have already been sent, and the size of the write that will occur if this check * does not encounter an out of order response. * * @param request The current request. * @param connection The connection used to send the current request. * @param inputStream The response stream, this may be used to check for an early response. * @param totalBytesSent Number of bytes that have already been sent. * @param nextWriteSize The size of a socket write operation that will follow this check. * @return True if an early response was detected, otherwise false. * @throws IOException in case of a network failure while checking for an early response. */ boolean isEarlyResponseDetected( ClassicHttpRequest request, HttpClientConnection connection, InputStream inputStream, long totalBytesSent, long nextWriteSize) throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/support/0040775 0000000 0000000 00000000000 14403631147 022707 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/io/support/package-info.java0100664 0000000 0000000 00000002417 14245617503 026103 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Support classes for the classic (blocking) I/O model. */ package org.apache.hc.core5.http.io.support; ././@LongLink0100644 0000000 0000000 00000000145 14245617503 011642 Lustar 0000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerExpectationDecorator.javahttpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerExpectationDecorator.java0100664 0000000 0000000 00000007126 14245617503 032700 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.support; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.HttpServerRequestHandler; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * {@link HttpServerRequestHandler} implementation that adds support * for the Expect-Continue handshake to an existing * {@link HttpServerRequestHandler}. * * @since 5.0 */ public class BasicHttpServerExpectationDecorator implements HttpServerRequestHandler { private final HttpServerRequestHandler requestHandler; public BasicHttpServerExpectationDecorator(final HttpServerRequestHandler requestHandler) { this.requestHandler = Args.notNull(requestHandler, "Request handler"); } /** * Verifies the HTTP request and decides whether it meets server expectations and the request * processing can continue. * * @param request the incoming HTTP request. * @param context the actual execution context. * @return {@code null} if the request meets expectations or a final HTTP response * with an error status representing the cause of expectation failure. */ protected ClassicHttpResponse verify(final ClassicHttpRequest request, final HttpContext context) { return null; } @Override public final void handle( final ClassicHttpRequest request, final ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { final Header expect = request.getFirstHeader(HttpHeaders.EXPECT); if (expect != null && HeaderElements.CONTINUE.equalsIgnoreCase(expect.getValue())) { final ClassicHttpResponse response = verify(request, context); if (response == null) { responseTrigger.sendInformation(new BasicClassicHttpResponse(HttpStatus.SC_CONTINUE)); } else { responseTrigger.submitResponse(response); return; } } requestHandler.handle(request, responseTrigger, context); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/support/HttpServerExpectationFilter.java0100664 0000000 0000000 00000010522 14245617503 031233 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.support; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.HttpFilterChain; import org.apache.hc.core5.http.io.HttpFilterHandler; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; /** * HttpServerExpectationFilter add support for the Expect-Continue handshake * to the request processing pipeline. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class HttpServerExpectationFilter implements HttpFilterHandler { /** * Verifies the HTTP request and decides whether it meets server expectations and the request * processing can continue. * * @param request the incoming HTTP request. * @param context the actual execution context. * @return {@code true} if the request meets expectations or {@code false} otherwise. */ protected boolean verify(final ClassicHttpRequest request, final HttpContext context) throws HttpException { return true; } /** * Generates response content entity for the final HTTP response with an error status * representing the cause of expectation failure. * * @param expectationFailed the final HTTP response. * @return the content entity for the final HTTP response with an error status * representing the cause of expectation failure. */ protected HttpEntity generateResponseContent(final HttpResponse expectationFailed) throws HttpException { return null; } @Override public final void handle( final ClassicHttpRequest request, final HttpFilterChain.ResponseTrigger responseTrigger, final HttpContext context, final HttpFilterChain chain) throws HttpException, IOException { final Header expect = request.getFirstHeader(HttpHeaders.EXPECT); final boolean expectContinue = expect != null && HeaderElements.CONTINUE.equalsIgnoreCase(expect.getValue()); if (expectContinue) { final boolean verified = verify(request, context); if (verified) { responseTrigger.sendInformation(new BasicClassicHttpResponse(HttpStatus.SC_CONTINUE)); } else { final ClassicHttpResponse expectationFailed = new BasicClassicHttpResponse(HttpStatus.SC_EXPECTATION_FAILED); final HttpEntity responseContent = generateResponseContent(expectationFailed); expectationFailed.setEntity(responseContent); responseTrigger.submitResponse(expectationFailed); return; } } chain.proceed(request, responseTrigger, context); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilder.java0100664 0000000 0000000 00000031126 14403631147 030013 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.support; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.HttpEntities; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.support.AbstractRequestBuilder; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * Builder for {@link ClassicHttpRequest} instances. *

* Please note that this class treats parameters differently depending on composition * of the request: if the request has a content entity explicitly set with * {@link #setEntity(HttpEntity)} or it is not an entity enclosing method * (such as POST or PUT), parameters will be added to the query component * of the request URI. Otherwise, parameters will be added as a URL encoded entity. *

* * @since 5.0 */ public class ClassicRequestBuilder extends AbstractRequestBuilder { private HttpEntity entity; ClassicRequestBuilder(final String method) { super(method); } ClassicRequestBuilder(final Method method) { super(method); } ClassicRequestBuilder(final String method, final URI uri) { super(method, uri); } ClassicRequestBuilder(final Method method, final URI uri) { super(method, uri); } ClassicRequestBuilder(final Method method, final String uri) { super(method, uri); } ClassicRequestBuilder(final String method, final String uri) { super(method, uri); } public static ClassicRequestBuilder create(final String method) { Args.notBlank(method, "HTTP method"); return new ClassicRequestBuilder(method); } public static ClassicRequestBuilder get() { return new ClassicRequestBuilder(Method.GET); } public static ClassicRequestBuilder get(final URI uri) { return new ClassicRequestBuilder(Method.GET, uri); } public static ClassicRequestBuilder get(final String uri) { return new ClassicRequestBuilder(Method.GET, uri); } public static ClassicRequestBuilder head() { return new ClassicRequestBuilder(Method.HEAD); } public static ClassicRequestBuilder head(final URI uri) { return new ClassicRequestBuilder(Method.HEAD, uri); } public static ClassicRequestBuilder head(final String uri) { return new ClassicRequestBuilder(Method.HEAD, uri); } public static ClassicRequestBuilder patch() { return new ClassicRequestBuilder(Method.PATCH); } public static ClassicRequestBuilder patch(final URI uri) { return new ClassicRequestBuilder(Method.PATCH, uri); } public static ClassicRequestBuilder patch(final String uri) { return new ClassicRequestBuilder(Method.PATCH, uri); } public static ClassicRequestBuilder post() { return new ClassicRequestBuilder(Method.POST); } public static ClassicRequestBuilder post(final URI uri) { return new ClassicRequestBuilder(Method.POST, uri); } public static ClassicRequestBuilder post(final String uri) { return new ClassicRequestBuilder(Method.POST, uri); } public static ClassicRequestBuilder put() { return new ClassicRequestBuilder(Method.PUT); } public static ClassicRequestBuilder put(final URI uri) { return new ClassicRequestBuilder(Method.PUT, uri); } public static ClassicRequestBuilder put(final String uri) { return new ClassicRequestBuilder(Method.PUT, uri); } public static ClassicRequestBuilder delete() { return new ClassicRequestBuilder(Method.DELETE); } public static ClassicRequestBuilder delete(final URI uri) { return new ClassicRequestBuilder(Method.DELETE, uri); } public static ClassicRequestBuilder delete(final String uri) { return new ClassicRequestBuilder(Method.DELETE, uri); } public static ClassicRequestBuilder trace() { return new ClassicRequestBuilder(Method.TRACE); } public static ClassicRequestBuilder trace(final URI uri) { return new ClassicRequestBuilder(Method.TRACE, uri); } public static ClassicRequestBuilder trace(final String uri) { return new ClassicRequestBuilder(Method.TRACE, uri); } public static ClassicRequestBuilder options() { return new ClassicRequestBuilder(Method.OPTIONS); } public static ClassicRequestBuilder options(final URI uri) { return new ClassicRequestBuilder(Method.OPTIONS, uri); } public static ClassicRequestBuilder options(final String uri) { return new ClassicRequestBuilder(Method.OPTIONS, uri); } /** * @since 5.1 */ public static ClassicRequestBuilder copy(final ClassicHttpRequest request) { Args.notNull(request, "HTTP request"); final ClassicRequestBuilder builder = new ClassicRequestBuilder(request.getMethod()); builder.digest(request); return builder; } protected void digest(final ClassicHttpRequest request) { super.digest(request); setEntity(request.getEntity()); } @Override public ClassicRequestBuilder setVersion(final ProtocolVersion version) { super.setVersion(version); return this; } @Override public ClassicRequestBuilder setUri(final URI uri) { super.setUri(uri); return this; } @Override public ClassicRequestBuilder setUri(final String uri) { super.setUri(uri); return this; } @Override public ClassicRequestBuilder setScheme(final String scheme) { super.setScheme(scheme); return this; } @Override public ClassicRequestBuilder setAuthority(final URIAuthority authority) { super.setAuthority(authority); return this; } /** * @since 5.1 */ @Override public ClassicRequestBuilder setHttpHost(final HttpHost httpHost) { super.setHttpHost(httpHost); return this; } @Override public ClassicRequestBuilder setPath(final String path) { super.setPath(path); return this; } @Override public ClassicRequestBuilder setHeaders(final Header... headers) { super.setHeaders(headers); return this; } @Override public ClassicRequestBuilder addHeader(final Header header) { super.addHeader(header); return this; } @Override public ClassicRequestBuilder addHeader(final String name, final String value) { super.addHeader(name, value); return this; } @Override public ClassicRequestBuilder removeHeader(final Header header) { super.removeHeader(header); return this; } @Override public ClassicRequestBuilder removeHeaders(final String name) { super.removeHeaders(name); return this; } @Override public ClassicRequestBuilder setHeader(final Header header) { super.setHeader(header); return this; } @Override public ClassicRequestBuilder setHeader(final String name, final String value) { super.setHeader(name, value); return this; } @Override public ClassicRequestBuilder setCharset(final Charset charset) { super.setCharset(charset); return this; } @Override public ClassicRequestBuilder addParameter(final NameValuePair nvp) { super.addParameter(nvp); return this; } @Override public ClassicRequestBuilder addParameter(final String name, final String value) { super.addParameter(name, value); return this; } @Override public ClassicRequestBuilder addParameters(final NameValuePair... nvps) { super.addParameters(nvps); return this; } @Override public ClassicRequestBuilder setAbsoluteRequestUri(final boolean absoluteRequestUri) { super.setAbsoluteRequestUri(absoluteRequestUri); return this; } public HttpEntity getEntity() { return entity; } public ClassicRequestBuilder setEntity(final HttpEntity entity) { this.entity = entity; return this; } public ClassicRequestBuilder setEntity(final String content, final ContentType contentType) { this.entity = new StringEntity(content, contentType); return this; } public ClassicRequestBuilder setEntity(final String content) { this.entity = new StringEntity(content); return this; } public ClassicRequestBuilder setEntity(final byte[] content, final ContentType contentType) { this.entity = new ByteArrayEntity(content, contentType); return this; } @Override public ClassicHttpRequest build() { String path = getPath(); if (TextUtils.isEmpty(path)) { path = "/"; } HttpEntity entityCopy = this.entity; final String method = getMethod(); final List parameters = getParameters(); if (parameters != null && !parameters.isEmpty()) { if (entityCopy == null && (Method.POST.isSame(method) || Method.PUT.isSame(method))) { entityCopy = HttpEntities.createUrlEncoded(parameters, getCharset()); } else { try { final URI uri = new URIBuilder(path) .setCharset(getCharset()) .addParameters(parameters) .build(); path = uri.toASCIIString(); } catch (final URISyntaxException ex) { // should never happen } } } if (entityCopy != null && Method.TRACE.isSame(method)) { throw new IllegalStateException(Method.TRACE + " requests may not include an entity"); } final BasicClassicHttpRequest result = new BasicClassicHttpRequest(method, getScheme(), getAuthority(), path); result.setVersion(getVersion()); result.setHeaders(getHeaders()); result.setEntity(entityCopy); result.setAbsoluteRequestUri(isAbsoluteRequestUri()); return result; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("ClassicRequestBuilder [method="); builder.append(getMethod()); builder.append(", scheme="); builder.append(getScheme()); builder.append(", authority="); builder.append(getAuthority()); builder.append(", path="); builder.append(getPath()); builder.append(", parameters="); builder.append(getParameters()); builder.append(", headerGroup="); builder.append(Arrays.toString(getHeaders())); builder.append(", entity="); builder.append(entity != null ? entity.getClass() : null); builder.append("]"); return builder.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerRequestHandler.java0100664 0000000 0000000 00000006631 14245617503 031500 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.support; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequestMapper; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.HttpServerRequestHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Basic {@link HttpServerRequestHandler} implementation that makes use of * {@link HttpRequestMapper} to dispatch the request to a particular * {@link HttpRequestHandler} for processing. * * @since 5.0 */ public class BasicHttpServerRequestHandler implements HttpServerRequestHandler { private final HttpRequestMapper handlerMapper; private final HttpResponseFactory responseFactory; public BasicHttpServerRequestHandler( final HttpRequestMapper handlerMapper, final HttpResponseFactory responseFactory) { this.handlerMapper = Args.notNull(handlerMapper, "Handler mapper"); this.responseFactory = responseFactory != null ? responseFactory : DefaultClassicHttpResponseFactory.INSTANCE; } public BasicHttpServerRequestHandler(final HttpRequestMapper handlerMapper) { this(handlerMapper, null); } @Override public void handle( final ClassicHttpRequest request, final ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { final ClassicHttpResponse response = responseFactory.newHttpResponse(HttpStatus.SC_OK); final HttpRequestHandler handler = handlerMapper != null ? handlerMapper.resolve(request, context) : null; if (handler != null) { handler.handle(request, response, context); } else { response.setCode(HttpStatus.SC_NOT_IMPLEMENTED); } responseTrigger.submitResponse(response); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicResponseBuilder.java0100664 0000000 0000000 00000012447 14403631147 030166 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.support; import java.util.Arrays; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.support.AbstractResponseBuilder; import org.apache.hc.core5.util.Args; /** * Builder for {@link ClassicHttpResponse} instances. * * @since 5.0 */ public class ClassicResponseBuilder extends AbstractResponseBuilder { private HttpEntity entity; ClassicResponseBuilder(final int status) { super(status); } public static ClassicResponseBuilder create(final int status) { Args.checkRange(status, 100, 599, "HTTP status code"); return new ClassicResponseBuilder(status); } /** * @since 5.1 */ public static ClassicResponseBuilder copy(final ClassicHttpResponse response) { Args.notNull(response, "HTTP response"); final ClassicResponseBuilder builder = new ClassicResponseBuilder(response.getCode()); builder.digest(response); return builder; } protected void digest(final ClassicHttpResponse response) { super.digest(response); setEntity(response.getEntity()); } @Override public ClassicResponseBuilder setVersion(final ProtocolVersion version) { super.setVersion(version); return this; } @Override public ClassicResponseBuilder setHeaders(final Header... headers) { super.setHeaders(headers); return this; } @Override public ClassicResponseBuilder addHeader(final Header header) { super.addHeader(header); return this; } @Override public ClassicResponseBuilder addHeader(final String name, final String value) { super.addHeader(name, value); return this; } @Override public ClassicResponseBuilder removeHeader(final Header header) { super.removeHeader(header); return this; } @Override public ClassicResponseBuilder removeHeaders(final String name) { super.removeHeaders(name); return this; } @Override public ClassicResponseBuilder setHeader(final Header header) { super.setHeader(header); return this; } @Override public ClassicResponseBuilder setHeader(final String name, final String value) { super.setHeader(name, value); return this; } public HttpEntity getEntity() { return entity; } public ClassicResponseBuilder setEntity(final HttpEntity entity) { this.entity = entity; return this; } public ClassicResponseBuilder setEntity(final String content, final ContentType contentType) { this.entity = new StringEntity(content, contentType); return this; } public ClassicResponseBuilder setEntity(final String content) { this.entity = new StringEntity(content); return this; } public ClassicResponseBuilder setEntity(final byte[] content, final ContentType contentType) { this.entity = new ByteArrayEntity(content, contentType); return this; } @Override public ClassicHttpResponse build() { final BasicClassicHttpResponse result = new BasicClassicHttpResponse(getStatus()); result.setVersion(getVersion()); result.setHeaders(getHeaders()); result.setEntity(entity); return result; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("ClassicResponseBuilder [status="); builder.append(getStatus()); builder.append(", headerGroup="); builder.append(Arrays.toString(getHeaders())); builder.append(", entity="); builder.append(entity != null ? entity.getClass() : null); builder.append("]"); return builder.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/support/AbstractHttpServerAuthFilter.java0100664 0000000 0000000 00000015303 14245617503 031337 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.support; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.HttpFilterChain; import org.apache.hc.core5.http.io.HttpFilterHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIAuthority; /** * Abstract HTTP request filter that implements standard HTTP authentication handshake. * * @param authorization token representation. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public abstract class AbstractHttpServerAuthFilter implements HttpFilterHandler { private final boolean respondImmediately; protected AbstractHttpServerAuthFilter(final boolean respondImmediately) { this.respondImmediately = respondImmediately; } /** * Parses authorization header value into an authentication token sent by the client * as a response to an authentication challenge. * * @param authorizationValue the authorization header value. * @param context the actual execution context. * @return authorization token */ protected abstract T parseChallengeResponse(String authorizationValue, HttpContext context) throws HttpException; /** * Authenticates the client using the authentication token sent by the client * as a response to an authentication challenge. * * @param challengeResponse the authentication token sent by the client * as a response to an authentication challenge. * @param authority the URI authority. * @param requestUri the request URI. * @param context the actual execution context. * @return {@code true} if the client could be successfully authenticated {@code false} otherwise. */ protected abstract boolean authenticate(T challengeResponse, URIAuthority authority, String requestUri, HttpContext context); /** * Generates an authentication challenge in case of unsuccessful authentication. * * @param challengeResponse the authentication token sent by the client * as a response to an authentication challenge * or {@code null} if the client has not sent any. * @param authority the URI authority. * @param requestUri the request URI. * @param context the actual execution context. * @return an authorization challenge value. */ protected abstract String generateChallenge(T challengeResponse, URIAuthority authority, String requestUri, HttpContext context); /** * Generates response body for UNAUTHORIZED response. * * @param unauthorized the response to return as a result of authentication failure. * @return the response content entity. */ protected HttpEntity generateResponseContent(final HttpResponse unauthorized) { return new StringEntity("Unauthorized"); } @Override public final void handle( final ClassicHttpRequest request, final HttpFilterChain.ResponseTrigger responseTrigger, final HttpContext context, final HttpFilterChain chain) throws HttpException, IOException { final Header h = request.getFirstHeader(HttpHeaders.AUTHORIZATION); final T challengeResponse = h != null ? parseChallengeResponse(h.getValue(), context) : null; final URIAuthority authority = request.getAuthority(); final String requestUri = request.getRequestUri(); final boolean authenticated = authenticate(challengeResponse, authority, requestUri, context); final Header expect = request.getFirstHeader(HttpHeaders.EXPECT); final boolean expectContinue = expect != null && HeaderElements.CONTINUE.equalsIgnoreCase(expect.getValue()); if (authenticated) { if (expectContinue) { responseTrigger.sendInformation(new BasicClassicHttpResponse(HttpStatus.SC_CONTINUE)); } chain.proceed(request, responseTrigger, context); } else { final ClassicHttpResponse unauthorized = new BasicClassicHttpResponse(HttpStatus.SC_UNAUTHORIZED); unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, generateChallenge(challengeResponse, authority, requestUri, context)); final HttpEntity responseContent = generateResponseContent(unauthorized); unauthorized.setEntity(responseContent); if (respondImmediately || expectContinue || request.getEntity() == null) { // Respond immediately responseTrigger.submitResponse(unauthorized); // Consume request body later EntityUtils.consume(request.getEntity()); } else { // Consume request body first EntityUtils.consume(request.getEntity()); // Respond later responseTrigger.submitResponse(unauthorized); } } } } ././@LongLink0100644 0000000 0000000 00000000145 14245617503 011642 Lustar 0000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/io/support/HttpServerFilterChainRequestHandler.javahttpcore5/src/main/java/org/apache/hc/core5/http/io/support/HttpServerFilterChainRequestHandler.java0100664 0000000 0000000 00000005365 14245617503 032652 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.support; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.io.HttpFilterChain; import org.apache.hc.core5.http.io.HttpServerRequestHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * {@link HttpServerRequestHandler} implementation that delegates request processing * to a {@link HttpServerFilterChainElement}. * * @since 5.0 */ public class HttpServerFilterChainRequestHandler implements HttpServerRequestHandler { private final HttpServerFilterChainElement filterChain; public HttpServerFilterChainRequestHandler(final HttpServerFilterChainElement filterChain) { this.filterChain = Args.notNull(filterChain, "Filter chain"); } @Override public void handle( final ClassicHttpRequest request, final ResponseTrigger trigger, final HttpContext context) throws HttpException, IOException { filterChain.handle(request, new HttpFilterChain.ResponseTrigger() { @Override public void sendInformation(final ClassicHttpResponse response) throws HttpException, IOException { trigger.sendInformation(response); } @Override public void submitResponse(final ClassicHttpResponse response) throws HttpException, IOException { trigger.submitResponse(response); } }, context); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/support/TerminalServerFilter.java0100664 0000000 0000000 00000006772 14245617503 027677 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.support; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequestMapper; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory; import org.apache.hc.core5.http.io.HttpFilterChain; import org.apache.hc.core5.http.io.HttpFilterHandler; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * {@link HttpFilterHandler} implementation represents a terminal handler * in a request processing pipeline that makes use of {@link HttpRequestMapper} * to dispatch the request to a particular {@link HttpRequestHandler}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public final class TerminalServerFilter implements HttpFilterHandler { private final HttpRequestMapper handlerMapper; private final HttpResponseFactory responseFactory; public TerminalServerFilter( final HttpRequestMapper handlerMapper, final HttpResponseFactory responseFactory) { this.handlerMapper = Args.notNull(handlerMapper, "Handler mapper"); this.responseFactory = responseFactory != null ? responseFactory : DefaultClassicHttpResponseFactory.INSTANCE; } @Override public void handle( final ClassicHttpRequest request, final HttpFilterChain.ResponseTrigger responseTrigger, final HttpContext context, final HttpFilterChain chain) throws HttpException, IOException { final ClassicHttpResponse response = responseFactory.newHttpResponse(HttpStatus.SC_OK); final HttpRequestHandler handler = handlerMapper.resolve(request, context); if (handler != null) { handler.handle(request, response, context); } else { response.setCode(HttpStatus.SC_NOT_IMPLEMENTED); } responseTrigger.submitResponse(response); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/support/HttpServerFilterChainElement.java0100664 0000000 0000000 00000004763 14403631147 031312 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.support; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.io.HttpFilterChain; import org.apache.hc.core5.http.io.HttpFilterHandler; import org.apache.hc.core5.http.protocol.HttpContext; /** * An element in a request processing chain. * * @since 5.0 */ public final class HttpServerFilterChainElement { private final HttpFilterHandler handler; private final HttpServerFilterChainElement next; private final HttpFilterChain filterChain; public HttpServerFilterChainElement(final HttpFilterHandler handler, final HttpServerFilterChainElement next) { this.handler = handler; this.next = next; this.filterChain = next != null ? next::handle : null; } public void handle( final ClassicHttpRequest request, final HttpFilterChain.ResponseTrigger responseTrigger, final HttpContext context) throws IOException, HttpException { handler.handle(request, responseTrigger, context, filterChain); } @Override public String toString() { return "{" + "handler=" + handler.getClass() + ", next=" + (next != null ? next.handler.getClass() : "null") + '}'; } }httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpFilterChain.java0100664 0000000 0000000 00000005467 14245617503 025103 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.protocol.HttpContext; /** * HttpFilterChain represents a single element in the server side request processing chain. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpFilterChain { /** * Response trigger that can be used to generate the final HTTP response * and terminate HTTP request processing. */ interface ResponseTrigger { /** * Sends an intermediate informational HTTP response to the client. * * @param response an intermediate (1xx) HTTP response. */ void sendInformation(ClassicHttpResponse response) throws HttpException, IOException; /** * Sends a final HTTP response to the client. * * @param response a final (non 1xx) HTTP response. */ void submitResponse(ClassicHttpResponse response) throws HttpException, IOException; } /** * Proceeds to the next element in the request processing chain. * * @param request the actual request. * @param responseTrigger the response trigger. * @param context the actual execution context. */ void proceed( ClassicHttpRequest request, ResponseTrigger responseTrigger, HttpContext context) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpMessageParser.java0100664 0000000 0000000 00000004125 14403631147 025436 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.MessageHeaders; /** * Message parser intended to build HTTP message head from an input stream. * * @param * {@link MessageHeaders} or a subclass * * @since 4.0 */ public interface HttpMessageParser { /** * Generates an instance of {@link MessageHeaders} from the given input stream.. * * @param buffer Session input buffer * @param inputStream Input stream * @return HTTP message head or {@code null} if the input stream has been * closed by the opposite endpoint. * @throws IOException in case of an I/O error * @throws HttpException in case of HTTP protocol violation */ T parse(SessionInputBuffer buffer, InputStream inputStream) throws IOException, HttpException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpFilterHandler.java0100664 0000000 0000000 00000005002 14245617503 025417 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.protocol.HttpContext; /** * HttpFilterHandler represents a routine for handling all incoming requests * in the server side request processing chain. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpFilterHandler { /** * Processes the incoming HTTP request and if processing has been completed * submits a final response to the client. The handler must not use * the response trigger after passing control to the next filter with the * {@link HttpFilterChain#proceed(ClassicHttpRequest, HttpFilterChain.ResponseTrigger, HttpContext)} * method. * * @param request the actual request. * @param responseTrigger the response trigger. * @param context the actual execution context. * @param chain the next element in the request processing chain. */ void handle( ClassicHttpRequest request, HttpFilterChain.ResponseTrigger responseTrigger, HttpContext context, HttpFilterChain chain) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/SessionInputBuffer.java0100664 0000000 0000000 00000013426 14245617503 025642 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.util.CharArrayBuffer; /** * Session input buffer for HTTP/1.1 blocking connections. *

* This interface facilitates intermediate buffering of input data streamed from * an input stream and provides methods for reading lines of text. * * @since 4.0 */ public interface SessionInputBuffer { /** * Returns length data stored in the buffer * * @return data length */ int length(); /** * Returns total capacity of the buffer * * @return total capacity */ int capacity(); /** * Returns available space in the buffer. * * @return available space. */ int available(); /** * Reads up to {@code len} bytes of data from the session buffer into * an array of bytes. An attempt is made to read as many as * {@code len} bytes, but a smaller number may be read, possibly * zero. The number of bytes actually read is returned as an integer. * *

This method blocks until input data is available, end of file is * detected, or an exception is thrown. * *

If {@code off} is negative, or {@code len} is negative, or * {@code off+len} is greater than the length of the array * {@code b}, then an {@code IndexOutOfBoundsException} is * thrown. * * @param b the buffer into which the data is read. * @param off the start offset in array {@code b} * at which the data is written. * @param len the maximum number of bytes to read. * @param inputStream Input stream * @return the total number of bytes read into the buffer, or * {@code -1} if there is no more data because the end of * the stream has been reached. * @throws IOException if an I/O error occurs. */ int read(byte[] b, int off, int len, InputStream inputStream) throws IOException; /** * Reads some number of bytes from the session buffer and stores them into * the buffer array {@code b}. The number of bytes actually read is * returned as an integer. This method blocks until input data is * available, end of file is detected, or an exception is thrown. * * @param b the buffer into which the data is read. * @param inputStream Input stream * @return the total number of bytes read into the buffer, or * {@code -1} is there is no more data because the end of * the stream has been reached. * @throws IOException if an I/O error occurs. */ int read(byte[] b, InputStream inputStream) throws IOException; /** * Reads the next byte of data from this session buffer. The value byte is * returned as an {@code int} in the range {@code 0} to * {@code 255}. If no byte is available because the end of the stream * has been reached, the value {@code -1} is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown. * * @param inputStream Input stream * @return the next byte of data, or {@code -1} if the end of the * stream is reached. * @throws IOException if an I/O error occurs. */ int read(InputStream inputStream) throws IOException; /** * Reads a complete line of characters up to a line delimiter from this * session buffer into the given line buffer. The number of chars actually * read is returned as an integer. The line delimiter itself is discarded. * If no char is available because the end of the stream has been reached, * the value {@code -1} is returned. This method blocks until input * data is available, end of file is detected, or an exception is thrown. *

* The choice of a char encoding and line delimiter sequence is up to the * specific implementations of this interface. * * @param buffer the line buffer, one line of characters upon return * @param inputStream Input stream * @return the total number of bytes read into the buffer, or * {@code -1} is there is no more data because the end of * the stream has been reached. * @throws IOException if an I/O error occurs. */ int readLine(CharArrayBuffer buffer, InputStream inputStream) throws IOException; /** * Returns {@link HttpTransportMetrics} for this session buffer. * * @return transport metrics. */ HttpTransportMetrics getMetrics(); } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpClientResponseHandler.java0100664 0000000 0000000 00000003656 14403631147 027140 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; /** * Handler that encapsulates the process of generating a response object * from a {@link ClassicHttpResponse}. * * * @since 4.0 */ @FunctionalInterface public interface HttpClientResponseHandler { /** * Processes an {@link ClassicHttpResponse} and returns some value * corresponding to that response. * * @param response The response to process * @return A value determined by the response * * @throws IOException in case of a problem or the connection was aborted */ T handleResponse(ClassicHttpResponse response) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpConnectionFactory.java0100664 0000000 0000000 00000002770 14245617503 026334 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import java.net.Socket; import org.apache.hc.core5.http.HttpConnection; /** * Factory for {@link HttpConnection} instances. * * @since 4.3 */ public interface HttpConnectionFactory { T createConnection(Socket socket) throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpMessageParserFactory.java0100664 0000000 0000000 00000002775 14245617503 027003 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import org.apache.hc.core5.http.MessageHeaders; import org.apache.hc.core5.http.config.Http1Config; /** * Factory for {@link HttpMessageParser} instances. * * @since 4.3 */ public interface HttpMessageParserFactory { HttpMessageParser create(Http1Config http1Config); } httpcore5/src/main/java/org/apache/hc/core5/http/io/SocketConfig.java0100664 0000000 0000000 00000026751 14435411677 024435 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.net.SocketAddress; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Classic I/O network socket configuration. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class SocketConfig { private static final Timeout DEFAULT_SOCKET_TIMEOUT = Timeout.ofMinutes(3); public static final SocketConfig DEFAULT = new Builder().build(); private final Timeout soTimeout; private final boolean soReuseAddress; private final TimeValue soLinger; private final boolean soKeepAlive; private final boolean tcpNoDelay; private final int sndBufSize; private final int rcvBufSize; private final int backlogSize; private final SocketAddress socksProxyAddress; SocketConfig( final Timeout soTimeout, final boolean soReuseAddress, final TimeValue soLinger, final boolean soKeepAlive, final boolean tcpNoDelay, final int sndBufSize, final int rcvBufSize, final int backlogSize, final SocketAddress socksProxyAddress) { super(); this.soTimeout = soTimeout; this.soReuseAddress = soReuseAddress; this.soLinger = soLinger; this.soKeepAlive = soKeepAlive; this.tcpNoDelay = tcpNoDelay; this.sndBufSize = sndBufSize; this.rcvBufSize = rcvBufSize; this.backlogSize = backlogSize; this.socksProxyAddress = socksProxyAddress; } /** * @see Builder#setSoTimeout(Timeout) */ public Timeout getSoTimeout() { return soTimeout; } /** * @see Builder#setSoReuseAddress(boolean) */ public boolean isSoReuseAddress() { return soReuseAddress; } /** * @see Builder#setSoLinger(TimeValue) */ public TimeValue getSoLinger() { return soLinger; } /** * @see Builder#setSoKeepAlive(boolean) */ public boolean isSoKeepAlive() { return soKeepAlive; } /** * @see Builder#setTcpNoDelay(boolean) */ public boolean isTcpNoDelay() { return tcpNoDelay; } /** * @see Builder#setSndBufSize(int) * @since 4.4 */ public int getSndBufSize() { return sndBufSize; } /** * @see Builder#setRcvBufSize(int) * @since 4.4 */ public int getRcvBufSize() { return rcvBufSize; } /** * @see Builder#setBacklogSize(int) * @since 4.4 */ public int getBacklogSize() { return backlogSize; } /** * @see Builder#setSocksProxyAddress(SocketAddress) */ public SocketAddress getSocksProxyAddress() { return this.socksProxyAddress; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("[soTimeout=").append(this.soTimeout) .append(", soReuseAddress=").append(this.soReuseAddress) .append(", soLinger=").append(this.soLinger) .append(", soKeepAlive=").append(this.soKeepAlive) .append(", tcpNoDelay=").append(this.tcpNoDelay) .append(", sndBufSize=").append(this.sndBufSize) .append(", rcvBufSize=").append(this.rcvBufSize) .append(", backlogSize=").append(this.backlogSize) .append(", socksProxyAddress=").append(this.socksProxyAddress) .append("]"); return builder.toString(); } public static SocketConfig.Builder custom() { return new Builder(); } public static SocketConfig.Builder copy(final SocketConfig config) { Args.notNull(config, "Socket config"); return new Builder() .setSoTimeout(config.getSoTimeout()) .setSoReuseAddress(config.isSoReuseAddress()) .setSoLinger(config.getSoLinger()) .setSoKeepAlive(config.isSoKeepAlive()) .setTcpNoDelay(config.isTcpNoDelay()) .setSndBufSize(config.getSndBufSize()) .setRcvBufSize(config.getRcvBufSize()) .setBacklogSize(config.getBacklogSize()) .setSocksProxyAddress(config.getSocksProxyAddress()); } public static class Builder { private Timeout soTimeout; private boolean soReuseAddress; private TimeValue soLinger; private boolean soKeepAlive; private boolean tcpNoDelay; private int sndBufSize; private int rcvBufSize; private int backlogSize; private SocketAddress socksProxyAddress; Builder() { this.soTimeout = DEFAULT_SOCKET_TIMEOUT; this.soReuseAddress = false; this.soLinger = TimeValue.NEG_ONE_SECOND; this.soKeepAlive = false; this.tcpNoDelay = true; this.sndBufSize = 0; this.rcvBufSize = 0; this.backlogSize = 0; this.socksProxyAddress = null; } /** * @see #setSoTimeout(Timeout) */ public Builder setSoTimeout(final int soTimeout, final TimeUnit timeUnit) { this.soTimeout = Timeout.of(soTimeout, timeUnit); return this; } /** * Determines the default socket timeout value for blocking I/O operations. *

* Default: 3 minutes *

* * @return the default socket timeout value for blocking I/O operations. * @see java.net.SocketOptions#SO_TIMEOUT */ public Builder setSoTimeout(final Timeout soTimeout) { this.soTimeout = soTimeout; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#SO_REUSEADDR} parameter * for newly created sockets. *

* Default: {@code false} *

* * @return the default value of the {@link java.net.SocketOptions#SO_REUSEADDR} parameter. * @see java.net.SocketOptions#SO_REUSEADDR */ public Builder setSoReuseAddress(final boolean soReuseAddress) { this.soReuseAddress = soReuseAddress; return this; } /** * @see #setSoLinger(TimeValue) */ public Builder setSoLinger(final int soLinger, final TimeUnit timeUnit) { this.soLinger = Timeout.of(soLinger, timeUnit); return this; } /** * Determines the default value of the {@link java.net.SocketOptions#SO_LINGER} parameter * for newly created sockets. *

* Default: {@code -1} *

* * @return the default value of the {@link java.net.SocketOptions#SO_LINGER} parameter. * @see java.net.SocketOptions#SO_LINGER */ public Builder setSoLinger(final TimeValue soLinger) { this.soLinger = soLinger; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#SO_KEEPALIVE} parameter * for newly created sockets. *

* Default: {@code false} *

* * @return the default value of the {@link java.net.SocketOptions#SO_KEEPALIVE} parameter. * @see java.net.SocketOptions#SO_KEEPALIVE */ public Builder setSoKeepAlive(final boolean soKeepAlive) { this.soKeepAlive = soKeepAlive; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#TCP_NODELAY} parameter * for newly created sockets. *

* Default: {@code false} *

* * @return the default value of the {@link java.net.SocketOptions#TCP_NODELAY} parameter. * @see java.net.SocketOptions#TCP_NODELAY */ public Builder setTcpNoDelay(final boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#SO_SNDBUF} parameter * for newly created sockets. *

* Default: {@code 0} (system default) *

* * @return the default value of the {@link java.net.SocketOptions#SO_SNDBUF} parameter. * @see java.net.SocketOptions#SO_SNDBUF * @since 4.4 */ public Builder setSndBufSize(final int sndBufSize) { this.sndBufSize = sndBufSize; return this; } /** * Determines the default value of the {@link java.net.SocketOptions#SO_RCVBUF} parameter * for newly created sockets. *

* Default: {@code 0} (system default) *

* * @return the default value of the {@link java.net.SocketOptions#SO_RCVBUF} parameter. * @see java.net.SocketOptions#SO_RCVBUF * @since 4.4 */ public Builder setRcvBufSize(final int rcvBufSize) { this.rcvBufSize = rcvBufSize; return this; } /** * Determines the maximum queue length for incoming connection indications * (a request to connect) also known as server socket backlog. *

* Default: {@code 0} (system default) *

* @return the maximum queue length for incoming connection indications * @since 4.4 */ public Builder setBacklogSize(final int backlogSize) { this.backlogSize = backlogSize; return this; } /** * The address of the SOCKS proxy to use. */ public Builder setSocksProxyAddress(final SocketAddress socksProxyAddress) { this.socksProxyAddress = socksProxyAddress; return this; } public SocketConfig build() { return new SocketConfig( Timeout.defaultsToDisabled(soTimeout), soReuseAddress, soLinger != null ? soLinger : TimeValue.NEG_ONE_SECOND, soKeepAlive, tcpNoDelay, sndBufSize, rcvBufSize, backlogSize, socksProxyAddress); } } } httpcore5/src/main/java/org/apache/hc/core5/http/io/ssl/0040775 0000000 0000000 00000000000 14245617503 022000 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/io/ssl/DefaultTlsSetupHandler.java0100664 0000000 0000000 00000003416 14245617503 027232 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.ssl; import javax.net.ssl.SSLParameters; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.http.ssl.TlsCiphers; /** * Default TLS session setup handler. * * @since 5.0 */ public final class DefaultTlsSetupHandler implements Callback { @Override public void execute(final SSLParameters sslParameters) { sslParameters.setProtocols(TLS.excludeWeak(sslParameters.getProtocols())); sslParameters.setCipherSuites(TlsCiphers.excludeWeak(sslParameters.getCipherSuites())); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/ssl/SSLSessionVerifier.java0100664 0000000 0000000 00000003037 14245617503 026344 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.ssl; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import org.apache.hc.core5.http.HttpHost; /** * Callback interface that can be used to customize TLS/SSL session verification. * * @since 5.0 */ public interface SSLSessionVerifier { void verify(HttpHost endpoint, SSLSession sslSession) throws SSLException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpServerConnection.java0100664 0000000 0000000 00000006431 14403631147 026165 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; /** * A server-side HTTP connection, which can be used for receiving * requests and sending responses. * * @since 4.0 */ public interface HttpServerConnection extends BHttpConnection { /** * Receives the request line and all headers available from this connection. * The caller should examine the returned request and decide if to receive a * request entity as well. * * @return a new HttpRequest object whose request line and headers are * initialized or {@code null} if the connection has been closed * by the opposite endpoint. * @throws HttpException in case of HTTP protocol violation * @throws IOException in case of an I/O error */ ClassicHttpRequest receiveRequestHeader() throws HttpException, IOException; /** * Receives the next request entity available from this connection and attaches it to * an existing request. * @param request the request to attach the entity to. * @throws HttpException in case of HTTP protocol violation * @throws IOException in case of an I/O error */ void receiveRequestEntity(ClassicHttpRequest request) throws HttpException, IOException; /** * Sends the response line and headers of a response over this connection. * @param response the response whose headers to send. * @throws HttpException in case of HTTP protocol violation * @throws IOException in case of an I/O error */ void sendResponseHeader(ClassicHttpResponse response) throws HttpException, IOException; /** * Sends the response entity of a response over this connection. * @param response the response whose entity to send. * @throws HttpException in case of HTTP protocol violation * @throws IOException in case of an I/O error */ void sendResponseEntity(ClassicHttpResponse response) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpRequestHandler.java0100664 0000000 0000000 00000004712 14245617503 025631 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.protocol.HttpContext; /** * HttpRequestHandler represents a routine for processing of a specific group * of HTTP requests. Protocol handlers are designed to take care of protocol * specific aspects, whereas individual request handlers are expected to take * care of application specific HTTP processing. The main purpose of a request * handler is to generate a response object with a content entity to be sent * back to the client in response to the given request * * @since 4.0 */ public interface HttpRequestHandler { /** * Handles the request and produces a response to be sent back to * the client. * * @param request the HTTP request. * @param response the HTTP response. * @param context the HTTP execution context. * @throws IOException in case of an I/O error. * @throws HttpException in case of HTTP protocol violation or a processing * problem. */ void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpMessageWriter.java0100664 0000000 0000000 00000003661 14245617503 025466 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import java.io.OutputStream; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.MessageHeaders; /** * Message writer intended to serialize HTTP message head to an output stream. * * @since 4.0 */ public interface HttpMessageWriter { /** * Serializes an instance of {@link MessageHeaders} to the given output stream. * * @param message HTTP message head * @param buffer session output buffer * @throws IOException in case of an I/O error * @throws HttpException in case of HTTP protocol violation */ void write(T message, SessionOutputBuffer buffer, OutputStream outputStream) throws IOException, HttpException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpMessageWriterFactory.java0100664 0000000 0000000 00000002662 14245617503 027016 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import org.apache.hc.core5.http.MessageHeaders; /** * Factory for {@link HttpMessageWriter} instances. * * @since 4.3 */ public interface HttpMessageWriterFactory { HttpMessageWriter create(); } httpcore5/src/main/java/org/apache/hc/core5/http/io/BHttpConnection.java0100664 0000000 0000000 00000006015 14403631147 025076 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.util.Timeout; /** * Abstract blocking HTTP connection interface. * * @since 5.0 */ public interface BHttpConnection extends HttpConnection { /** * Checks if input data is available from the connection. May wait for * the specified time until some data becomes available. Note that some * implementations may completely ignore the timeout parameter. * * @param timeout the maximum time to wait for data * @return true if data is available; false if there was no data available * even after waiting for {@code timeout}. * @throws IOException if an error happens on the connection */ boolean isDataAvailable(Timeout timeout) throws IOException; /** * Checks whether this connection has gone down. * Network connections may get closed during some time of inactivity * for several reasons. The next time a read is attempted on such a * connection it will throw an IOException. * This method tries to alleviate this inconvenience by trying to * find out if a connection is still usable. Implementations may do * that by attempting a read with a very small timeout. Thus this * method may block for a small amount of time before returning a result. * It is therefore an expensive operation. * * @return {@code true} if attempts to use this connection are likely * to fail and this connection should be closed, * or {@code false} if they are likely to succeed */ boolean isStale() throws IOException; /** * Writes out all pending buffered data over the open connection. * * @throws java.io.IOException in case of an I/O error */ void flush() throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/0040775 0000000 0000000 00000000000 14435411677 022520 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/package-info.java0100664 0000000 0000000 00000002434 14245617503 025702 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP message entity APIs based on the classic (blocking) I/O model. */ package org.apache.hc.core5.http.io.entity; httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/ByteBufferEntity.java0100664 0000000 0000000 00000006062 14245617503 026611 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * An entity that delivers the contents of a {@link ByteBuffer}. */ public class ByteBufferEntity extends AbstractHttpEntity { private final ByteBuffer buffer; private final long length; public ByteBufferEntity(final ByteBuffer buffer, final ContentType contentType, final String contentEncoding) { super(contentType, contentEncoding); Args.notNull(buffer, "Source byte buffer"); this.buffer = buffer; this.length = buffer.remaining(); } public ByteBufferEntity(final ByteBuffer buffer, final ContentType contentType) { this(buffer, contentType, null); } @Override public final boolean isRepeatable() { return false; } @Override public final long getContentLength() { return length; } @Override public final InputStream getContent() throws IOException, UnsupportedOperationException { return new InputStream() { @Override public int read() throws IOException { if (!buffer.hasRemaining()) { return -1; } return buffer.get() & 0xFF; } @Override public int read(final byte[] bytes, final int off, final int len) throws IOException { if (!buffer.hasRemaining()) { return -1; } final int chunk = Math.min(len, buffer.remaining()); buffer.get(bytes, off, chunk); return chunk; } }; } @Override public final boolean isStreaming() { return false; } @Override public final void close() throws IOException { } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/EmptyInputStream.java0100664 0000000 0000000 00000005032 14245617503 026645 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.InputStream; /** * @since 5.1 */ public final class EmptyInputStream extends InputStream { public static final EmptyInputStream INSTANCE = new EmptyInputStream(); private EmptyInputStream() { // noop. } /** * Returns {@code 0}. */ @Override public int available() { return 0; } /** * Noop. */ @Override public void close() { // noop. } /** * Noop. */ @SuppressWarnings("sync-override") @Override public void mark(final int readLimit) { // noop. } /** * Returns {@code true}. */ @Override public boolean markSupported() { return true; } /** * Returns {@code -1}. */ @Override public int read() { return -1; } /** * Returns {@code -1}. */ @Override public int read(final byte[] buf) { return -1; } /** * Returns {@code -1}. */ @Override public int read(final byte[] buf, final int off, final int len) { return -1; } /** * Noop. */ @SuppressWarnings("sync-override") @Override public void reset() { // noop } /** * Returns {@code 0}. */ @Override public long skip(final long n) { return 0L; } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/NullEntity.java0100664 0000000 0000000 00000005617 14245617503 025473 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collections; import java.util.List; import java.util.Set; /** * An empty entity with no content-type. This type may be used for convenience * in place of an empty {@link ByteArrayEntity}. * * @since 5.1 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class NullEntity implements HttpEntity { public static final NullEntity INSTANCE = new NullEntity(); private NullEntity() {} @Override public boolean isRepeatable() { return true; } @Override public InputStream getContent() throws IOException, UnsupportedOperationException { return EmptyInputStream.INSTANCE; } @Override public void writeTo(final OutputStream outStream) throws IOException {} @Override public boolean isStreaming() { return false; } @Override public Supplier> getTrailers() { return null; } @Override public void close() throws IOException {} @Override public long getContentLength() { return 0; } @Override public String getContentType() { return null; } @Override public String getContentEncoding() { return null; } @Override public boolean isChunked() { return false; } @Override public Set getTrailerNames() { return Collections.emptySet(); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/FileEntity.java0100664 0000000 0000000 00000005113 14245617503 025427 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * A self contained, repeatable entity that obtains its content from a file. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class FileEntity extends AbstractHttpEntity { private final File file; public FileEntity(final File file, final ContentType contentType, final String contentEncoding) { super(contentType, contentEncoding); this.file = Args.notNull(file, "File"); } public FileEntity(final File file, final ContentType contentType) { super(contentType, null); this.file = Args.notNull(file, "File"); } @Override public final boolean isRepeatable() { return true; } @Override public final long getContentLength() { return this.file.length(); } @Override public final InputStream getContent() throws IOException { return new FileInputStream(this.file); } @Override public final boolean isStreaming() { return false; } @Override public final void close() throws IOException { // do nothing } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntities.java0100664 0000000 0000000 00000020254 14403631147 025776 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.nio.charset.Charset; import java.nio.file.Path; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.zip.GZIPOutputStream; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.io.IOCallback; import org.apache.hc.core5.net.WWWFormCodec; import org.apache.hc.core5.util.Args; /** * {HttpEntity} factory methods. * * @since 5.0 */ public final class HttpEntities { private HttpEntities() { } public static HttpEntity create(final String content, final ContentType contentType) { return new StringEntity(content, contentType); } public static HttpEntity create(final String content, final Charset charset) { return new StringEntity(content, ContentType.TEXT_PLAIN.withCharset(charset)); } public static HttpEntity create(final String content) { return new StringEntity(content, ContentType.TEXT_PLAIN); } public static HttpEntity create(final byte[] content, final ContentType contentType) { return new ByteArrayEntity(content, contentType); } public static HttpEntity create(final File content, final ContentType contentType) { return new FileEntity(content, contentType); } public static HttpEntity create(final Serializable serializable, final ContentType contentType) { return new SerializableEntity(serializable, contentType); } public static HttpEntity createUrlEncoded( final Iterable parameters, final Charset charset) { final ContentType contentType = charset != null ? ContentType.APPLICATION_FORM_URLENCODED.withCharset(charset) : ContentType.APPLICATION_FORM_URLENCODED; return create(WWWFormCodec.format(parameters, contentType.getCharset()), contentType); } public static HttpEntity create(final IOCallback callback, final ContentType contentType) { return new EntityTemplate(-1, contentType, null, callback); } public static HttpEntity gzip(final HttpEntity entity) { return new HttpEntityWrapper(entity) { @Override public String getContentEncoding() { return "gzip"; } @Override public long getContentLength() { return -1; } @Override public InputStream getContent() throws IOException { throw new UnsupportedOperationException(); } @Override public void writeTo(final OutputStream outStream) throws IOException { Args.notNull(outStream, "Output stream"); final GZIPOutputStream gzip = new GZIPOutputStream(outStream); super.writeTo(gzip); // Only close output stream if the wrapped entity has been // successfully written out gzip.close(); } }; } public static HttpEntity createGzipped(final String content, final ContentType contentType) { return gzip(create(content, contentType)); } public static HttpEntity createGzipped(final String content, final Charset charset) { return gzip(create(content, charset)); } public static HttpEntity createGzipped(final String content) { return gzip(create(content)); } public static HttpEntity createGzipped(final byte[] content, final ContentType contentType) { return gzip(create(content, contentType)); } public static HttpEntity createGzipped(final File content, final ContentType contentType) { return gzip(create(content, contentType)); } public static HttpEntity createGzipped(final Serializable serializable, final ContentType contentType) { return gzip(create(serializable, contentType)); } public static HttpEntity createGzipped(final IOCallback callback, final ContentType contentType) { return gzip(create(callback, contentType)); } public static HttpEntity createGzipped(final Path content, final ContentType contentType) { return gzip(create(content, contentType)); } public static HttpEntity withTrailers(final HttpEntity entity, final Header... trailers) { return new HttpEntityWrapper(entity) { @Override public boolean isChunked() { // Must be chunk coded return true; } @Override public long getContentLength() { return -1; } @Override public Supplier> getTrailers() { return () -> Arrays.asList(trailers); } @Override public Set getTrailerNames() { final Set names = new LinkedHashSet<>(); for (final Header trailer: trailers) { names.add(trailer.getName()); } return names; } }; } public static HttpEntity create(final String content, final ContentType contentType, final Header... trailers) { return withTrailers(create(content, contentType), trailers); } public static HttpEntity create(final String content, final Charset charset, final Header... trailers) { return withTrailers(create(content, charset), trailers); } public static HttpEntity create(final String content, final Header... trailers) { return withTrailers(create(content), trailers); } public static HttpEntity create(final byte[] content, final ContentType contentType, final Header... trailers) { return withTrailers(create(content, contentType), trailers); } public static HttpEntity create(final File content, final ContentType contentType, final Header... trailers) { return withTrailers(create(content, contentType), trailers); } public static HttpEntity create( final Serializable serializable, final ContentType contentType, final Header... trailers) { return withTrailers(create(serializable, contentType), trailers); } public static HttpEntity create( final IOCallback callback, final ContentType contentType, final Header... trailers) { return withTrailers(create(callback, contentType), trailers); } public static HttpEntity create(final Path content, final ContentType contentType) { return new PathEntity(content, contentType); } public static HttpEntity create(final Path content, final ContentType contentType, final Header... trailers) { return withTrailers(create(content, contentType), trailers); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/BufferedHttpEntity.java0100664 0000000 0000000 00000007270 14245617503 027140 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.util.Args; /** * A wrapping entity that buffers it content if necessary. * The buffered entity is always repeatable. * If the wrapped entity is repeatable itself, calls are passed through. * If the wrapped entity is not repeatable, the content is read into a * buffer once and provided from there as often as required. * * @since 4.0 */ public class BufferedHttpEntity extends HttpEntityWrapper { private final byte[] buffer; /** * Creates a new buffered entity wrapper. * * @param entity the entity to wrap, not null * @throws IllegalArgumentException if wrapped is null */ public BufferedHttpEntity(final HttpEntity entity) throws IOException { super(entity); if (!entity.isRepeatable() || entity.getContentLength() < 0) { final ByteArrayOutputStream out = new ByteArrayOutputStream(); entity.writeTo(out); out.flush(); this.buffer = out.toByteArray(); } else { this.buffer = null; } } @Override public long getContentLength() { if (this.buffer != null) { return this.buffer.length; } return super.getContentLength(); } @Override public InputStream getContent() throws IOException { if (this.buffer != null) { return new ByteArrayInputStream(this.buffer); } return super.getContent(); } /** * Tells that this entity does not have to be chunked. * * @return {@code false} */ @Override public boolean isChunked() { return (buffer == null) && super.isChunked(); } /** * Tells that this entity is repeatable. * * @return {@code true} */ @Override public boolean isRepeatable() { return true; } @Override public void writeTo(final OutputStream outStream) throws IOException { Args.notNull(outStream, "Output stream"); if (this.buffer != null) { outStream.write(this.buffer); } else { super.writeTo(outStream); } } // non-javadoc, see interface HttpEntity @Override public boolean isStreaming() { return (buffer == null) && super.isStreaming(); } } // class BufferedHttpEntity httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/PathEntity.java0100664 0000000 0000000 00000005257 14245617503 025455 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * A self contained, repeatable entity that obtains its content from a path. */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class PathEntity extends AbstractHttpEntity { private final Path path; public PathEntity(final Path path, final ContentType contentType, final String contentEncoding) { super(contentType, contentEncoding); this.path = Args.notNull(path, "Path"); } public PathEntity(final Path path, final ContentType contentType) { super(contentType, null); this.path = Args.notNull(path, "Path"); } @Override public final boolean isRepeatable() { return true; } @Override public final long getContentLength() { try { return Files.size(this.path); } catch (final IOException e) { throw new IllegalStateException(e); } } @Override public final InputStream getContent() throws IOException { return Files.newInputStream(path); } @Override public final boolean isStreaming() { return false; } @Override public final void close() throws IOException { // do nothing } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWrapper.java0100664 0000000 0000000 00000007137 14245617503 027040 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Set; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.util.Args; /** * Base class for wrapping entities that delegates all calls to the wrapped entity. * Implementations can derive from this class and override only those methods that * should not be delegated to the wrapped entity. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class HttpEntityWrapper implements HttpEntity { /** The wrapped entity. */ private final HttpEntity wrappedEntity; /** * Creates a new entity wrapper. * * @param wrappedEntity the entity to wrap. */ public HttpEntityWrapper(final HttpEntity wrappedEntity) { super(); this.wrappedEntity = Args.notNull(wrappedEntity, "Wrapped entity"); } @Override public boolean isRepeatable() { return wrappedEntity.isRepeatable(); } @Override public boolean isChunked() { return wrappedEntity.isChunked(); } @Override public long getContentLength() { return wrappedEntity.getContentLength(); } @Override public String getContentType() { return wrappedEntity.getContentType(); } @Override public String getContentEncoding() { return wrappedEntity.getContentEncoding(); } @Override public InputStream getContent() throws IOException { return wrappedEntity.getContent(); } @Override public void writeTo(final OutputStream outStream) throws IOException { wrappedEntity.writeTo(outStream); } @Override public boolean isStreaming() { return wrappedEntity.isStreaming(); } @Override public Supplier> getTrailers() { return wrappedEntity.getTrailers(); } @Override public Set getTrailerNames() { return wrappedEntity.getTrailerNames(); } @Override public void close() throws IOException { wrappedEntity.close(); } @Override public String toString() { return "Wrapper [" + wrappedEntity + "]"; } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractHttpEntity.java0100664 0000000 0000000 00000010657 14245617503 027164 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collections; import java.util.List; import java.util.Set; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.util.Args; /** * Abstract base class for mutable entities. Provides the commonly used attributes for streamed and * self-contained implementations. * * @since 4.0 */ public abstract class AbstractHttpEntity implements HttpEntity { static final int OUTPUT_BUFFER_SIZE = 4096; private final String contentType; private final String contentEncoding; private final boolean chunked; protected AbstractHttpEntity(final String contentType, final String contentEncoding, final boolean chunked) { this.contentType = contentType; this.contentEncoding = contentEncoding; this.chunked = chunked; } protected AbstractHttpEntity(final ContentType contentType, final String contentEncoding, final boolean chunked) { this.contentType = contentType != null ? contentType.toString() : null; this.contentEncoding = contentEncoding; this.chunked = chunked; } protected AbstractHttpEntity(final String contentType, final String contentEncoding) { this(contentType, contentEncoding, false); } protected AbstractHttpEntity(final ContentType contentType, final String contentEncoding) { this(contentType, contentEncoding, false); } public static void writeTo(final HttpEntity entity, final OutputStream outStream) throws IOException { Args.notNull(entity, "Entity"); Args.notNull(outStream, "Output stream"); try (final InputStream inStream = entity.getContent()) { if (inStream != null) { int count; final byte[] tmp = new byte[OUTPUT_BUFFER_SIZE]; while ((count = inStream.read(tmp)) != -1) { outStream.write(tmp, 0, count); } } } } @Override public void writeTo(final OutputStream outStream) throws IOException { writeTo(this, outStream); } @Override public final String getContentType() { return contentType; } @Override public final String getContentEncoding() { return contentEncoding; } @Override public final boolean isChunked() { return chunked; } @Override public boolean isRepeatable() { return false; } @Override public Supplier> getTrailers() { return null; } @Override public Set getTrailerNames() { return Collections.emptySet(); } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("[Entity-Class: "); sb.append(getClass().getSimpleName()); sb.append(", Content-Type: "); sb.append(contentType); sb.append(", Content-Encoding: "); sb.append(contentEncoding); sb.append(", chunked: "); sb.append(chunked); sb.append(']'); return sb.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/ByteArrayEntity.java0100664 0000000 0000000 00000011171 14403631147 026447 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * A self contained, repeatable entity that obtains its content from a byte array. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class ByteArrayEntity extends AbstractHttpEntity { private final byte[] b; private final int off, len; /** * @since 5.0 */ public ByteArrayEntity( final byte[] b, final int off, final int len, final ContentType contentType, final String contentEncoding, final boolean chunked) { super(contentType, contentEncoding, chunked); Args.notNull(b, "Source byte array"); Args.notNegative(off, "offset"); Args.notNegative(len, "length"); Args.notNegative(off + len, "off + len"); Args.check(off <= b.length, "off %s cannot be greater then b.length %s ", off, b.length); Args.check(off + len <= b.length, "off + len %s cannot be less then b.length %s ", off + len, b.length); this.b = b; this.off = off; this.len = len; } /** * @since 5.0 */ public ByteArrayEntity( final byte[] b, final int off, final int len, final ContentType contentType, final String contentEncoding) { this(b, off, len, contentType, contentEncoding, false); } /** * @since 5.0 */ public ByteArrayEntity( final byte[] b, final ContentType contentType, final String contentEncoding, final boolean chunked) { super(contentType, contentEncoding, chunked); Args.notNull(b, "Source byte array"); this.b = b; this.off = 0; this.len = this.b.length; } /** * @since 5.0 */ public ByteArrayEntity(final byte[] b, final ContentType contentType, final String contentEncoding) { this(b, contentType, contentEncoding, false); } public ByteArrayEntity(final byte[] b, final ContentType contentType, final boolean chunked) { this(b, contentType, null, chunked); } public ByteArrayEntity(final byte[] b, final ContentType contentType) { this(b, contentType, null, false); } public ByteArrayEntity( final byte[] b, final int off, final int len, final ContentType contentType, final boolean chunked) { this(b, off, len, contentType, null, chunked); } public ByteArrayEntity(final byte[] b, final int off, final int len, final ContentType contentType) { this(b, off, len, contentType, null, false); } @Override public final boolean isRepeatable() { return true; } @Override public final long getContentLength() { return this.len; } @Override public final InputStream getContent() { return new ByteArrayInputStream(this.b, this.off, this.len); } @Override public final void writeTo(final OutputStream outStream) throws IOException { Args.notNull(outStream, "Output stream"); outStream.write(this.b, this.off, this.len); outStream.flush(); } @Override public final boolean isStreaming() { return false; } @Override public final void close() throws IOException { } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/SerializableEntity.java0100664 0000000 0000000 00000006775 14245617503 027175 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * A streamed entity that obtains its content from a {@link Serializable}. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class SerializableEntity extends AbstractHttpEntity { private final Serializable serializable; /** * Creates new instance of this class. * * @param serializable the serializable object. * @param contentType the content type. * @param contentEncoding the content encoding. */ public SerializableEntity( final Serializable serializable, final ContentType contentType, final String contentEncoding) { super(contentType, contentEncoding); this.serializable = Args.notNull(serializable, "Source object"); } /** * Creates new instance of this class. * * @param serializable the serializable object. * @param contentType the content type. */ public SerializableEntity(final Serializable serializable, final ContentType contentType) { this(serializable, contentType, null); } @Override public final InputStream getContent() throws IOException, IllegalStateException { final ByteArrayOutputStream buf = new ByteArrayOutputStream(); writeTo(buf); return new ByteArrayInputStream(buf.toByteArray()); } @Override public final long getContentLength() { return -1; } @Override public final boolean isRepeatable() { return true; } @Override public final boolean isStreaming() { return false; } @Override public final void writeTo(final OutputStream outStream) throws IOException { Args.notNull(outStream, "Output stream"); final ObjectOutputStream out = new ObjectOutputStream(outStream); out.writeObject(this.serializable); out.flush(); } @Override public final void close() throws IOException { } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/StringEntity.java0100664 0000000 0000000 00000011152 14435411677 026023 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * A self contained, repeatable entity that obtains its content from a {@link String}. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class StringEntity extends AbstractHttpEntity { private final byte[] content; /** * Creates a StringEntity with the specified content and content type. * * @param string content to be used. Not {@code null}. * @param contentType content type to be used. May be {@code null}, in which case the default * MIME type {@link ContentType#TEXT_PLAIN} is assumed. * * @since 5.0 */ public StringEntity( final String string, final ContentType contentType, final String contentEncoding, final boolean chunked) { super(contentType, contentEncoding, chunked); Args.notNull(string, "Source string"); final Charset charset = ContentType.getCharset(contentType, StandardCharsets.ISO_8859_1); this.content = string.getBytes(charset); } public StringEntity(final String string, final ContentType contentType, final boolean chunked) { this(string, contentType, null, chunked); } public StringEntity(final String string, final ContentType contentType) { this(string, contentType, null, false); } /** * Creates a StringEntity with the specified content and charset. The MIME type defaults * to "text/plain". * * @param string content to be used. Not {@code null}. * @param charset character set to be used. May be {@code null}, in which case the default * is {@link StandardCharsets#ISO_8859_1} is assumed * * @since 4.2 */ public StringEntity(final String string, final Charset charset) { this(string, ContentType.TEXT_PLAIN.withCharset(charset)); } public StringEntity(final String string, final Charset charset, final boolean chunked) { this(string, ContentType.TEXT_PLAIN.withCharset(charset), chunked); } /** * Creates a StringEntity with the specified content. The content type defaults to * {@link ContentType#TEXT_PLAIN}. * * @param string content to be used. Not {@code null}. * * @throws IllegalArgumentException if the string parameter is null */ public StringEntity(final String string) { this(string, ContentType.DEFAULT_TEXT); } @Override public final boolean isRepeatable() { return true; } @Override public final long getContentLength() { return this.content.length; } @Override public final InputStream getContent() throws IOException { return new ByteArrayInputStream(this.content); } @Override public final void writeTo(final OutputStream outStream) throws IOException { Args.notNull(outStream, "Output stream"); outStream.write(this.content); outStream.flush(); } @Override public final boolean isStreaming() { return false; } @Override public final void close() throws IOException { // nothing to do } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/EntityTemplate.java0100664 0000000 0000000 00000005772 14245617503 026336 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.io.IOCallback; import org.apache.hc.core5.util.Args; /** * Entity that delegates the process of content generation to a {@link IOCallback} * with {@link OutputStream} as output sink. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public final class EntityTemplate extends AbstractHttpEntity { private final long contentLength; private final IOCallback callback; public EntityTemplate( final long contentLength, final ContentType contentType, final String contentEncoding, final IOCallback callback) { super(contentType, contentEncoding); this.contentLength = contentLength; this.callback = Args.notNull(callback, "I/O callback"); } @Override public long getContentLength() { return contentLength; } @Override public InputStream getContent() throws IOException { final ByteArrayOutputStream buf = new ByteArrayOutputStream(); writeTo(buf); return new ByteArrayInputStream(buf.toByteArray()); } @Override public boolean isRepeatable() { return true; } @Override public void writeTo(final OutputStream outStream) throws IOException { Args.notNull(outStream, "Output stream"); this.callback.execute(outStream); } @Override public boolean isStreaming() { return false; } @Override public void close() throws IOException { } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/BasicHttpEntity.java0100664 0000000 0000000 00000007017 14245617503 026436 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.util.Args; /** * A generic streamed, non-repeatable entity that obtains its content from an {@link InputStream}. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class BasicHttpEntity extends AbstractHttpEntity { private final InputStream content; private final long length; public BasicHttpEntity( final InputStream content, final long length, final ContentType contentType, final String contentEncoding, final boolean chunked) { super(contentType, contentEncoding, chunked); this.content = Args.notNull(content, "Content stream"); this.length = length; } public BasicHttpEntity( final InputStream content, final long length, final ContentType contentType, final String contentEncoding) { this(content, length, contentType, contentEncoding, false); } public BasicHttpEntity(final InputStream content, final long length, final ContentType contentType) { this(content, length, contentType, null); } public BasicHttpEntity(final InputStream content, final ContentType contentType, final String contentEncoding) { this(content, -1, contentType, contentEncoding); } public BasicHttpEntity(final InputStream content, final ContentType contentType) { this(content, -1, contentType, null); } public BasicHttpEntity(final InputStream content, final ContentType contentType, final boolean chunked) { this(content, -1, contentType, null, chunked); } @Override public final long getContentLength() { return this.length; } @Override public final InputStream getContent() throws IllegalStateException { return this.content; } @Override public final boolean isRepeatable() { return false; } @Override public final boolean isStreaming() { return this.content != null && this.content != EmptyInputStream.INSTANCE; } @Override public final void close() throws IOException { Closer.close(content); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/InputStreamEntity.java0100664 0000000 0000000 00000010046 14245617503 027024 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * A streamed, non-repeatable entity that obtains its content from an {@link InputStream}. * * @since 4.0 */ public class InputStreamEntity extends AbstractHttpEntity { private final InputStream content; private final long length; public InputStreamEntity( final InputStream inStream, final long length, final ContentType contentType, final String contentEncoding) { super(contentType, contentEncoding); this.content = Args.notNull(inStream, "Source input stream"); this.length = length; } public InputStreamEntity(final InputStream inStream, final long length, final ContentType contentType) { this(inStream, length, contentType, null); } public InputStreamEntity(final InputStream inStream, final ContentType contentType) { this(inStream, -1, contentType, null); } @Override public final boolean isRepeatable() { return false; } /** * @return the content length or {@code -1} if unknown */ @Override public final long getContentLength() { return this.length; } @Override public final InputStream getContent() throws IOException { return this.content; } /** * Writes bytes from the {@code InputStream} this entity was constructed * with to an {@code OutputStream}. The content length * determines how many bytes are written. If the length is unknown ({@code -1}), the * stream will be completely consumed (to the end of the stream). * */ @Override public final void writeTo(final OutputStream outStream) throws IOException { Args.notNull(outStream, "Output stream"); try (final InputStream inStream = this.content) { final byte[] buffer = new byte[OUTPUT_BUFFER_SIZE]; int readLen; if (this.length < 0) { // consume until EOF while ((readLen = inStream.read(buffer)) != -1) { outStream.write(buffer, 0, readLen); } } else { // consume no more than length long remaining = this.length; while (remaining > 0) { readLen = inStream.read(buffer, 0, (int) Math.min(OUTPUT_BUFFER_SIZE, remaining)); if (readLen == -1) { break; } outStream.write(buffer, 0, readLen); remaining -= readLen; } } } } @Override public final boolean isStreaming() { return true; } @Override public final void close() throws IOException { content.close(); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/EntityUtils.java0100664 0000000 0000000 00000045330 14435411677 025662 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.net.WWWFormCodec; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; import org.apache.hc.core5.util.CharArrayBuffer; /** * Support methods for {@link HttpEntity}. * * @since 4.0 */ public final class EntityUtils { // TODO Consider using a sane value, but what is sane? 1 GB? 100 MB? 10 MB? private static final int DEFAULT_ENTITY_RETURN_MAX_LENGTH = Integer.MAX_VALUE; private static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1; private static final int DEFAULT_CHAR_BUFFER_SIZE = 1024; private static final int DEFAULT_BYTE_BUFFER_SIZE = 4096; private EntityUtils() { // NoOp } /** * Ensures that the entity content is fully consumed and the content stream, if exists, * is closed. The process is done, quietly , without throwing any IOException. * * @param entity the entity to consume. * * @since 4.2 */ public static void consumeQuietly(final HttpEntity entity) { try { consume(entity); } catch (final IOException ignore) { // Ignore exception } } /** * Ensures that the entity content is fully consumed and the content stream, if exists, * is closed. * * @param entity the entity to consume. * @throws IOException if an error occurs reading the input stream * * @since 4.1 */ public static void consume(final HttpEntity entity) throws IOException { if (entity == null) { return; } if (entity.isStreaming()) { Closer.close(entity.getContent()); } } /** * Gets a usable content length value for the given candidate. * * @param contentLength an integer. * @return The given content length or {@value #DEFAULT_BYTE_BUFFER_SIZE} if it is < 0. */ private static int toContentLength(final int contentLength) { return contentLength < 0 ? DEFAULT_BYTE_BUFFER_SIZE : contentLength; } /** * Reads the contents of an entity and return it as a byte array. * * @param entity the entity to read from= * @return byte array containing the entity content. May be null if * {@link HttpEntity#getContent()} is null. * @throws IOException if an error occurs reading the input stream * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE */ public static byte[] toByteArray(final HttpEntity entity) throws IOException { Args.notNull(entity, "HttpEntity"); final int contentLength = toContentLength((int) Args.checkContentLength(entity)); try (final InputStream inStream = entity.getContent()) { if (inStream == null) { return null; } final ByteArrayBuffer buffer = new ByteArrayBuffer(contentLength); final byte[] tmp = new byte[DEFAULT_BYTE_BUFFER_SIZE]; int l; while ((l = inStream.read(tmp)) != -1) { buffer.append(tmp, 0, l); } return buffer.toByteArray(); } } /** * Reads the contents of an entity and return it as a byte array. * * @param entity the entity to read from= * @return byte array containing the entity content. May be null if * {@link HttpEntity#getContent()} is null. * @param maxResultLength * The maximum size of the String to return; use it to guard against unreasonable or malicious processing. * @throws IOException if an error occurs reading the input stream * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE */ public static byte[] toByteArray(final HttpEntity entity, final int maxResultLength) throws IOException { Args.notNull(entity, "HttpEntity"); final int contentLength = toContentLength((int) Args.checkContentLength(entity)); try (final InputStream inStream = entity.getContent()) { if (inStream == null) { return null; } final ByteArrayBuffer buffer = new ByteArrayBuffer(Math.min(maxResultLength, contentLength)); final byte[] tmp = new byte[DEFAULT_BYTE_BUFFER_SIZE]; int l; while ((l = inStream.read(tmp)) != -1 && buffer.length() < maxResultLength) { buffer.append(tmp, 0, l); } buffer.setLength(Math.min(buffer.length(), maxResultLength)); return buffer.toByteArray(); } } private static CharArrayBuffer toCharArrayBuffer(final InputStream inStream, final int contentLength, final Charset charset, final int maxResultLength) throws IOException { Args.notNull(inStream, "InputStream"); Args.positive(maxResultLength, "maxResultLength"); final Charset actualCharset = charset == null ? DEFAULT_CHARSET : charset; final CharArrayBuffer buf = new CharArrayBuffer( Math.min(maxResultLength, contentLength > 0 ? contentLength : DEFAULT_CHAR_BUFFER_SIZE)); final Reader reader = new InputStreamReader(inStream, actualCharset); final char[] tmp = new char[DEFAULT_CHAR_BUFFER_SIZE]; int chReadCount; while ((chReadCount = reader.read(tmp)) != -1 && buf.length() < maxResultLength) { buf.append(tmp, 0, chReadCount); } buf.setLength(Math.min(buf.length(), maxResultLength)); return buf; } private static final Map CONTENT_TYPE_MAP; static { final ContentType[] contentTypes = { ContentType.APPLICATION_ATOM_XML, ContentType.APPLICATION_FORM_URLENCODED, ContentType.APPLICATION_JSON, ContentType.APPLICATION_SVG_XML, ContentType.APPLICATION_XHTML_XML, ContentType.APPLICATION_XML, ContentType.MULTIPART_FORM_DATA, ContentType.TEXT_HTML, ContentType.TEXT_PLAIN, ContentType.TEXT_XML }; final HashMap map = new HashMap<>(); for (final ContentType contentType: contentTypes) { map.put(contentType.getMimeType(), contentType); } CONTENT_TYPE_MAP = Collections.unmodifiableMap(map); } private static String toString(final HttpEntity entity, final ContentType contentType, final int maxResultLength) throws IOException { Args.notNull(entity, "HttpEntity"); final int contentLength = toContentLength((int) Args.checkContentLength(entity)); try (final InputStream inStream = entity.getContent()) { if (inStream == null) { return null; } Charset charset = null; if (contentType != null) { charset = contentType.getCharset(); if (charset == null) { final ContentType defaultContentType = CONTENT_TYPE_MAP.get(contentType.getMimeType()); charset = defaultContentType != null ? defaultContentType.getCharset() : null; } } return toCharArrayBuffer(inStream, contentLength, charset, maxResultLength).toString(); } } /** * Gets the entity content as a String, using the provided default character set * if none is found in the entity. * If defaultCharset is null, the default "ISO-8859-1" is used. * * @param entity must not be null * @param defaultCharset character set to be applied if none found in the entity, * or if the entity provided charset is invalid or not available. * @return the entity content as a String. May be null if * {@link HttpEntity#getContent()} is null. * @throws ParseException if header elements cannot be parsed * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE * @throws IOException if an error occurs reading the input stream * @throws java.nio.charset.UnsupportedCharsetException Thrown when the named entity's charset is not available in * this instance of the Java virtual machine and no defaultCharset is provided. */ public static String toString( final HttpEntity entity, final Charset defaultCharset) throws IOException, ParseException { return toString(entity, defaultCharset, DEFAULT_ENTITY_RETURN_MAX_LENGTH); } /** * Gets the entity content as a String, using the provided default character set * if none is found in the entity. * If defaultCharset is null, the default "ISO-8859-1" is used. * * @param entity must not be null * @param defaultCharset character set to be applied if none found in the entity, * or if the entity provided charset is invalid or not available. * @param maxResultLength * The maximum size of the String to return; use it to guard against unreasonable or malicious processing. * @return the entity content as a String. May be null if * {@link HttpEntity#getContent()} is null. * @throws ParseException if header elements cannot be parsed * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE * @throws IOException if an error occurs reading the input stream * @throws java.nio.charset.UnsupportedCharsetException Thrown when the named entity's charset is not available in * this instance of the Java virtual machine and no defaultCharset is provided. */ public static String toString( final HttpEntity entity, final Charset defaultCharset, final int maxResultLength) throws IOException, ParseException { Args.notNull(entity, "HttpEntity"); ContentType contentType = null; try { contentType = ContentType.parse(entity.getContentType()); } catch (final UnsupportedCharsetException ex) { if (defaultCharset == null) { throw new UnsupportedEncodingException(ex.getMessage()); } } if (contentType != null) { if (contentType.getCharset() == null) { contentType = contentType.withCharset(defaultCharset); } } else { contentType = ContentType.DEFAULT_TEXT.withCharset(defaultCharset); } return toString(entity, contentType, maxResultLength); } /** * Gets the entity content as a String, using the provided default character set * if none is found in the entity. * If defaultCharset is null, the default "ISO-8859-1" is used. * * @param entity must not be null * @param defaultCharset character set to be applied if none found in the entity * @return the entity content as a String. May be null if * {@link HttpEntity#getContent()} is null. * @throws ParseException if header elements cannot be parsed * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE * @throws IOException if an error occurs reading the input stream * @throws java.nio.charset.UnsupportedCharsetException Thrown when the named charset is not available in * this instance of the Java virtual machine */ public static String toString( final HttpEntity entity, final String defaultCharset) throws IOException, ParseException { return toString(entity, defaultCharset, DEFAULT_ENTITY_RETURN_MAX_LENGTH); } /** * Gets the entity content as a String, using the provided default character set * if none is found in the entity. * If defaultCharset is null, the default "ISO-8859-1" is used. * * @param entity must not be null * @param defaultCharset character set to be applied if none found in the entity * @param maxResultLength * The maximum size of the String to return; use it to guard against unreasonable or malicious processing. * @return the entity content as a String. May be null if * {@link HttpEntity#getContent()} is null. * @throws ParseException if header elements cannot be parsed * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE * @throws IOException if an error occurs reading the input stream * @throws java.nio.charset.UnsupportedCharsetException Thrown when the named charset is not available in * this instance of the Java virtual machine */ public static String toString( final HttpEntity entity, final String defaultCharset, final int maxResultLength) throws IOException, ParseException { return toString(entity, defaultCharset != null ? Charset.forName(defaultCharset) : null, maxResultLength); } /** * Reads the contents of an entity and return it as a String. * The content is converted using the character set from the entity (if any), * failing that, "ISO-8859-1" is used. * * @param entity the entity to convert to a string; must not be null * @return String containing the content. * @throws ParseException if header elements cannot be parsed * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE * @throws IOException if an error occurs reading the input stream * @throws java.nio.charset.UnsupportedCharsetException Thrown when the named charset is not available in * this instance of the Java virtual machine */ public static String toString(final HttpEntity entity) throws IOException, ParseException { return toString(entity, DEFAULT_ENTITY_RETURN_MAX_LENGTH); } /** * Reads the contents of an entity and return it as a String. * The content is converted using the character set from the entity (if any), * failing that, "ISO-8859-1" is used. * * @param entity the entity to convert to a string; must not be null * @param maxResultLength * The maximum size of the String to return; use it to guard against unreasonable or malicious processing. * @return String containing the content. * @throws ParseException if header elements cannot be parsed * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE * @throws IOException if an error occurs reading the input stream * @throws java.nio.charset.UnsupportedCharsetException Thrown when the named charset is not available in * this instance of the Java virtual machine */ public static String toString(final HttpEntity entity, final int maxResultLength) throws IOException, ParseException { Args.notNull(entity, "HttpEntity"); return toString(entity, ContentType.parse(entity.getContentType()), maxResultLength); } /** * Returns a list of {@link NameValuePair NameValuePairs} as parsed from an {@link HttpEntity}. * The encoding is taken from the entity's Content-Encoding header. *

* This is typically used while parsing an HTTP POST. *

* * @param entity * The entity to parse * @return a list of {@link NameValuePair} as built from the URI's query portion. * @throws IOException * If there was an exception getting the entity's data. */ public static List parse(final HttpEntity entity) throws IOException { return parse(entity, DEFAULT_ENTITY_RETURN_MAX_LENGTH); } /** * Returns a list of {@link NameValuePair NameValuePairs} as parsed from an {@link HttpEntity}. * The encoding is taken from the entity's Content-Encoding header. *

* This is typically used while parsing an HTTP POST. *

* * @param entity * The entity to parse * @param maxStreamLength * The maximum size of the stream to read; use it to guard against unreasonable or malicious processing. * @return a list of {@link NameValuePair} as built from the URI's query portion. * @throws IOException * If there was an exception getting the entity's data. */ public static List parse(final HttpEntity entity, final int maxStreamLength) throws IOException { Args.notNull(entity, "HttpEntity"); final int contentLength = toContentLength((int) Args.checkContentLength(entity)); final ContentType contentType = ContentType.parse(entity.getContentType()); if (!ContentType.APPLICATION_FORM_URLENCODED.isSameMimeType(contentType)) { return Collections.emptyList(); } final Charset charset = contentType.getCharset(DEFAULT_CHARSET); final CharArrayBuffer buf; try (final InputStream inStream = entity.getContent()) { if (inStream == null) { return Collections.emptyList(); } buf = toCharArrayBuffer(inStream, contentLength, charset, maxStreamLength); } if (buf.isEmpty()) { return Collections.emptyList(); } return WWWFormCodec.parse(buf, charset); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpResponseInformationCallback.java0100664 0000000 0000000 00000003170 14245617503 030321 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; /** * Informational (1xx) HTTP response callback. * * @since 5.0 */ public interface HttpResponseInformationCallback { void execute(HttpResponse response, HttpConnection connection, HttpContext context) throws HttpException; } httpcore5/src/main/java/org/apache/hc/core5/http/io/EofSensorInputStream.java0100664 0000000 0000000 00000022177 14245617503 026147 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.util.Args; /** * A stream wrapper that triggers actions on {@link #close close()} and EOF. * Primarily used to auto-release an underlying managed connection when the response * body is consumed or no longer needed. * * @see EofSensorWatcher * * @since 4.0 */ // don't use FilterInputStream as the base class, we'd have to // override markSupported(), mark(), and reset() to disable them public class EofSensorInputStream extends InputStream { /** * The wrapped input stream, while accessible. * The value changes to {@code null} when the wrapped stream * becomes inaccessible. */ private InputStream wrappedStream; /** * Indicates whether this stream itself is closed. * If it isn't, but {@link #wrappedStream wrappedStream} * is {@code null}, we're running in EOF mode. * All read operations will indicate EOF without accessing * the underlying stream. After closing this stream, read * operations will trigger an {@link IOException IOException}. * * @see #isReadAllowed isReadAllowed */ private boolean selfClosed; /** The watcher to be notified, if any. */ private final EofSensorWatcher eofWatcher; /** * Creates a new EOF sensor. * If no watcher is passed, the underlying stream will simply be * closed when EOF is detected or {@link #close close} is called. * Otherwise, the watcher decides whether the underlying stream * should be closed before detaching from it. * * @param in the wrapped stream * @param watcher the watcher for events, or {@code null} for * auto-close behavior without notification */ public EofSensorInputStream(final InputStream in, final EofSensorWatcher watcher) { Args.notNull(in, "Wrapped stream"); wrappedStream = in; selfClosed = false; eofWatcher = watcher; } boolean isSelfClosed() { return selfClosed; } InputStream getWrappedStream() { return wrappedStream; } /** * Checks whether the underlying stream can be read from. * * @return {@code true} if the underlying stream is accessible, * {@code false} if this stream is in EOF mode and * detached from the underlying stream * * @throws IOException if this stream is already closed */ private boolean isReadAllowed() throws IOException { if (selfClosed) { throw new IOException("Attempted read on closed stream."); } return wrappedStream != null; } @Override public int read() throws IOException { int b = -1; if (isReadAllowed()) { try { b = wrappedStream.read(); checkEOF(b); } catch (final IOException ex) { checkAbort(); throw ex; } } return b; } @Override public int read(final byte[] b, final int off, final int len) throws IOException { int readLen = -1; if (isReadAllowed()) { try { readLen = wrappedStream.read(b, off, len); checkEOF(readLen); } catch (final IOException ex) { checkAbort(); throw ex; } } return readLen; } @Override public int read(final byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int available() throws IOException { int a = 0; // not -1 if (isReadAllowed()) { try { a = wrappedStream.available(); // no checkEOF() here, available() can't trigger EOF } catch (final IOException ex) { checkAbort(); throw ex; } } return a; } @Override public void close() throws IOException { // tolerate multiple calls to close() selfClosed = true; checkClose(); } /** * Detects EOF and notifies the watcher. * This method should only be called while the underlying stream is * still accessible. Use {@link #isReadAllowed isReadAllowed} to * check that condition. *

* If EOF is detected, the watcher will be notified and this stream * is detached from the underlying stream. This prevents multiple * notifications from this stream. *

* * @param eof the result of the calling read operation. * A negative value indicates that EOF is reached. * * @throws IOException * in case of an IO problem on closing the underlying stream */ private void checkEOF(final int eof) throws IOException { final InputStream toCheckStream = wrappedStream; if ((toCheckStream != null) && (eof < 0)) { try { boolean scws = true; // should close wrapped stream? if (eofWatcher != null) { scws = eofWatcher.eofDetected(toCheckStream); } if (scws) { toCheckStream.close(); } } finally { wrappedStream = null; } } } /** * Detects stream close and notifies the watcher. * There's not much to detect since this is called by {@link #close close}. * The watcher will only be notified if this stream is closed * for the first time and before EOF has been detected. * This stream will be detached from the underlying stream to prevent * multiple notifications to the watcher. * * @throws IOException * in case of an IO problem on closing the underlying stream */ private void checkClose() throws IOException { final InputStream toCloseStream = wrappedStream; if (toCloseStream != null) { try { boolean scws = true; // should close wrapped stream? if (eofWatcher != null) { scws = eofWatcher.streamClosed(toCloseStream); } if (scws) { toCloseStream.close(); } } finally { wrappedStream = null; } } } /** * Detects stream abort and notifies the watcher. * There's not much to detect since this is called by {@link #abort()}. * The watcher will only be notified if this stream is aborted * for the first time and before EOF has been detected or the * stream has been {@link #close closed} gracefully. * This stream will be detached from the underlying stream to prevent * multiple notifications to the watcher. * * @throws IOException * in case of an IO problem on closing the underlying stream */ private void checkAbort() throws IOException { final InputStream toAbortStream = wrappedStream; if (toAbortStream != null) { try { boolean scws = true; // should close wrapped stream? if (eofWatcher != null) { scws = eofWatcher.streamAbort(toAbortStream); } if (scws) { toAbortStream.close(); } } finally { wrappedStream = null; } } } /** * Aborts this stream. * This is a special version of {@link #close close()} which prevents * re-use of the underlying connection, if any. Calling this method * indicates that there should be no attempt to read until the end of * the stream. * * @throws IOException * in case of an IO problem on closing the underlying stream */ public void abort() throws IOException { // tolerate multiple calls selfClosed = true; checkAbort(); } } httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpServerRequestHandler.java0100664 0000000 0000000 00000006275 14245617503 027026 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.protocol.HttpContext; /** * HttpServerRequestHandler represents a routine for processing of a specific group * of HTTP requests. Request execution filters are designed to take care of protocol * specific aspects, whereas individual request handlers are expected to take * care of application specific HTTP processing. The main purpose of a request * handler is to generate a response object with a content entity to be sent * back to the client in response to the given request. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpServerRequestHandler { /** * Response trigger that can be used to submit a final HTTP response * and terminate HTTP request processing. */ interface ResponseTrigger { /** * Sends an intermediate informational HTTP response to the client. * * @param response the intermediate (1xx) HTTP response */ void sendInformation(ClassicHttpResponse response) throws HttpException, IOException; /** * Sends a final HTTP response to the client. * * @param response the final (non 1xx) HTTP response */ void submitResponse(ClassicHttpResponse response) throws HttpException, IOException; } /** * Handles the request and submits a final response to be sent back to the client. * * @param request the actual request. * @param responseTrigger the response trigger. * @param context the actual execution context. */ void handle( ClassicHttpRequest request, ResponseTrigger responseTrigger, HttpContext context) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/ContentTooLongException.java0100664 0000000 0000000 00000004236 14245617503 026230 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; /** * Signals that HTTP entity content is too long. * * @since 4.2 */ public class ContentTooLongException extends IOException { private static final long serialVersionUID = -924287689552495383L; /** * Creates a new ContentTooLongException with the specified detail message. * * @param message exception message */ public ContentTooLongException(final String message) { super(HttpException.clean(message)); } /** * Constructs a new ContentTooLongException with the specified detail message. * * @param format The exception detail message format; see {@link String#format(String, Object...)}. * @param args The exception detail message arguments; see {@link String#format(String, Object...)}. * * @since 4.4.11 */ public ContentTooLongException(final String format, final Object... args) { super(HttpException.clean(String.format(format, args))); } } httpcore5/src/main/java/org/apache/hc/core5/http/ProtocolException.java0100664 0000000 0000000 00000005316 14245617503 025115 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Signals that an HTTP protocol violation has occurred. * For example a malformed status line or headers, a missing message body, etc. * * @since 4.0 */ public class ProtocolException extends HttpException { private static final long serialVersionUID = -2143571074341228994L; /** * Creates a new ProtocolException with a {@code null} detail message. */ public ProtocolException() { super(); } /** * Creates a new ProtocolException with the specified detail message. * * @param message The exception detail message */ public ProtocolException(final String message) { super(message); } /** * Constructs a new ProtocolException with the specified detail message. * * @param format The exception detail message format; see {@link String#format(String, Object...)}. * @param args The exception detail message arguments; see {@link String#format(String, Object...)}. * * @since 5.0 */ public ProtocolException(final String format, final Object... args) { super(format, args); } /** * Creates a new ProtocolException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public ProtocolException(final String message, final Throwable cause) { super(message, cause); } } httpcore5/src/main/java/org/apache/hc/core5/http/StreamClosedException.java0100664 0000000 0000000 00000003115 14245617503 025674 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; /** * Signals that data stream has already been closed. * * @since 5.0 */ public class StreamClosedException extends IOException { private static final long serialVersionUID = 1L; public StreamClosedException() { super("Stream already closed"); } public StreamClosedException(final String message) { super(message); } } httpcore5/src/main/java/org/apache/hc/core5/http/HttpRequest.java0100664 0000000 0000000 00000006267 14245617503 023733 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.net.URI; import java.net.URISyntaxException; import org.apache.hc.core5.net.URIAuthority; /** * A request message from a client to a server includes, within the * first line of that message, the method to be applied to the resource, * the identifier of the resource, and the protocol version in use. * * @since 4.0 */ public interface HttpRequest extends HttpMessage { /** * Returns method of this request message. * * @return the request method. */ String getMethod(); /** * Returns URI path of this request message or {@code null} if not set. * * @return the request URI or {@code null}. */ String getPath(); /** * Sets URI path of this request message. * * @since 5.0 */ void setPath(String path); /** * Returns scheme of this request message. * * @return the scheme or {@code null}. * * @since 5.0 */ String getScheme(); /** * Sets scheme of this request message. * * @since 5.0 */ void setScheme(String scheme); /** * Returns authority of this request message. * * @return the authority or {@code null}. * * @since 5.0 */ URIAuthority getAuthority(); /** * Sets authority of this request message. * * @since 5.0 */ void setAuthority(URIAuthority authority); /** * Returns request URI of this request message. It may be an absolute or relative URI. * Applicable to HTTP/1.1 version or earlier. * * @return the request URI. * * @since 5.0 */ String getRequestUri(); /** * Returns full request URI of this request message. * * @return the request URI. * * @since 5.0 */ URI getUri() throws URISyntaxException; /** * Sets the full request URI of this request message. * * @param requestUri the request URI. * * @since 5.0 */ void setUri(final URI requestUri); } httpcore5/src/main/java/org/apache/hc/core5/http/HttpStatus.java0100664 0000000 0000000 00000026077 14245617503 023567 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Constants enumerating the HTTP status codes. * All status codes defined in RFC 7231 (HTTP/1.1), RFC 2518 (WebDAV), RFC 7540 (HTTP/2), * RFC 6585 (Additional HTTP Status Codes), RFC 8297 (Early Hints), RFC 7538 (Permanent Redirect), * RFC 7725 (An HTTP Status Code to Report Legal Obstacles) and RFC 2295 (Transparent Content * Negotiation) are listed. * * @see RFC 7231 (HTTP/1.1) * @see RFC 2518 (WebDAV) * @see RFC 7540 (HTTP/2) * @see RFC 6585 (Additional HTTP Status Codes) * @see RFC 8297 (Early Hints) * @see RFC 7538 (Permanent Redirect) * @see RFC 7725 (An HTTP Status Code to Report Legal Obstacles) * @see RFC 2295 (Transparent Content Negotiation) * @see RFC 2817 (Upgrading to TLS Within HTTP/1.1) * @see RFC 8470 (Using Early Data in HTTP) * @since 4.0 */ public final class HttpStatus { private HttpStatus() { // no instances. } // --- 1xx Informational --- /** {@code 100 1xx Informational} (HTTP/1.1 - RFC 7231) */ public static final int SC_INFORMATIONAL = 100; /** {@code 100 Continue} (HTTP/1.1 - RFC 7231) */ public static final int SC_CONTINUE = 100; /** {@code 101 Switching Protocols} (HTTP/1.1 - RFC 7231)*/ public static final int SC_SWITCHING_PROTOCOLS = 101; /** {@code 102 Processing} (WebDAV - RFC 2518) */ public static final int SC_PROCESSING = 102; /** {@code 103 Early Hints (Early Hints - RFC 8297)}*/ public static final int SC_EARLY_HINTS = 103; // --- 2xx Success --- /** {@code 2xx Success} (HTTP/1.0 - RFC 7231) */ public static final int SC_SUCCESS = 200; /** {@code 200 OK} (HTTP/1.0 - RFC 7231) */ public static final int SC_OK = 200; /** {@code 201 Created} (HTTP/1.0 - RFC 7231) */ public static final int SC_CREATED = 201; /** {@code 202 Accepted} (HTTP/1.0 - RFC 7231) */ public static final int SC_ACCEPTED = 202; /** {@code 203 Non Authoritative Information} (HTTP/1.1 - RFC 7231) */ public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203; /** {@code 204 No Content} (HTTP/1.0 - RFC 7231) */ public static final int SC_NO_CONTENT = 204; /** {@code 205 Reset Content} (HTTP/1.1 - RFC 7231) */ public static final int SC_RESET_CONTENT = 205; /** {@code 206 Partial Content} (HTTP/1.1 - RFC 7231) */ public static final int SC_PARTIAL_CONTENT = 206; /** * {@code 207 Multi-Status} (WebDAV - RFC 2518) * or * {@code 207 Partial Update OK} (HTTP/1.1 - draft-ietf-http-v11-spec-rev-01?) */ public static final int SC_MULTI_STATUS = 207; /** * {@code 208 Already Reported} (WebDAV - RFC 5842, p.30, section 7.1) */ public static final int SC_ALREADY_REPORTED = 208; /** * {@code 226 IM Used} (Delta encoding in HTTP - RFC 3229, p. 30, section 10.4.1) */ public static final int SC_IM_USED = 226; // --- 3xx Redirection --- /** {@code 3xx Redirection} (HTTP/1.1 - RFC 7231) */ public static final int SC_REDIRECTION = 300; /** {@code 300 Multiple Choices} (HTTP/1.1 - RFC 7231) */ public static final int SC_MULTIPLE_CHOICES = 300; /** {@code 301 Moved Permanently} (HTTP/1.0 - RFC 7231) */ public static final int SC_MOVED_PERMANENTLY = 301; /** {@code 302 Moved Temporarily} (Sometimes {@code Found}) (HTTP/1.0 - RFC 7231) */ public static final int SC_MOVED_TEMPORARILY = 302; /** {@code 303 See Other} (HTTP/1.1 - RFC 7231) */ public static final int SC_SEE_OTHER = 303; /** {@code 304 Not Modified} (HTTP/1.0 - RFC 7231) */ public static final int SC_NOT_MODIFIED = 304; /** {@code 305 Use Proxy} (HTTP/1.1 - RFC 7231) */ public static final int SC_USE_PROXY = 305; /** {@code 307 Temporary Redirect} (HTTP/1.1 - RFC 7231) */ public static final int SC_TEMPORARY_REDIRECT = 307; /** {@code 308 Permanent Redirect} (HTTP/1.1 - RFC 7538) */ public static final int SC_PERMANENT_REDIRECT = 308; // --- 4xx Client Error --- /** {@code 4xx Client Error} (HTTP/1.1 - RFC 7231) */ public static final int SC_CLIENT_ERROR = 400; /** {@code 400 Bad Request} (HTTP/1.1 - RFC 7231) */ public static final int SC_BAD_REQUEST = 400; /** {@code 401 Unauthorized} (HTTP/1.0 - RFC 7231) */ public static final int SC_UNAUTHORIZED = 401; /** {@code 402 Payment Required} (HTTP/1.1 - RFC 7231) */ public static final int SC_PAYMENT_REQUIRED = 402; /** {@code 403 Forbidden} (HTTP/1.0 - RFC 7231) */ public static final int SC_FORBIDDEN = 403; /** {@code 404 Not Found} (HTTP/1.0 - RFC 7231) */ public static final int SC_NOT_FOUND = 404; /** {@code 405 Method Not Allowed} (HTTP/1.1 - RFC 7231) */ public static final int SC_METHOD_NOT_ALLOWED = 405; /** {@code 406 Not Acceptable} (HTTP/1.1 - RFC 7231) */ public static final int SC_NOT_ACCEPTABLE = 406; /** {@code 407 Proxy Authentication Required} (HTTP/1.1 - RFC 7231)*/ public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407; /** {@code 408 Request Timeout} (HTTP/1.1 - RFC 7231) */ public static final int SC_REQUEST_TIMEOUT = 408; /** {@code 409 Conflict} (HTTP/1.1 - RFC 7231) */ public static final int SC_CONFLICT = 409; /** {@code 410 Gone} (HTTP/1.1 - RFC 7231) */ public static final int SC_GONE = 410; /** {@code 411 Length Required} (HTTP/1.1 - RFC 7231) */ public static final int SC_LENGTH_REQUIRED = 411; /** {@code 412 Precondition Failed} (HTTP/1.1 - RFC 7231) */ public static final int SC_PRECONDITION_FAILED = 412; /** {@code 413 Request Entity Too Large} (HTTP/1.1 - RFC 7231) */ public static final int SC_REQUEST_TOO_LONG = 413; /** {@code 414 Request-URI Too Long} (HTTP/1.1 - RFC 7231) */ public static final int SC_REQUEST_URI_TOO_LONG = 414; /** {@code 415 Unsupported Media Type} (HTTP/1.1 - RFC 7231) */ public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415; /** {@code 416 Requested Range Not Satisfiable} (HTTP/1.1 - RFC 7231) */ public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416; /** {@code 417 Expectation Failed} (HTTP/1.1 - RFC 7231) */ public static final int SC_EXPECTATION_FAILED = 417; /** {@code 421 Misdirected Request} (HTTP/2 - RFC 7540) */ public static final int SC_MISDIRECTED_REQUEST = 421; /** * Static constant for a 419 error. * {@code 419 Insufficient Space on Resource} * (WebDAV - draft-ietf-webdav-protocol-05?) * or {@code 419 Proxy Reauthentication Required} * (HTTP/1.1 drafts?) */ public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419; /** * Static constant for a 420 error. * {@code 420 Method Failure} * (WebDAV - draft-ietf-webdav-protocol-05?) */ public static final int SC_METHOD_FAILURE = 420; /** {@code 422 Unprocessable Entity} (WebDAV - RFC 2518) */ public static final int SC_UNPROCESSABLE_ENTITY = 422; /** {@code 423 Locked} (WebDAV - RFC 2518) */ public static final int SC_LOCKED = 423; /** {@code 424 Failed Dependency} (WebDAV - RFC 2518) */ public static final int SC_FAILED_DEPENDENCY = 424; /** {@code 425 Too Early} (Using Early Data in HTTP - RFC 8470) */ public static final int SC_TOO_EARLY = 425; /** {@code 426 Upgrade Dependency} (HTTP/1.1 - RFC 2817) */ public static final int SC_UPGRADE_REQUIRED = 426; /** {@code 428 Precondition Required} (Additional HTTP Status Codes - RFC 6585) */ public static final int SC_PRECONDITION_REQUIRED = 428; /** {@code 429 Too Many Requests} (Additional HTTP Status Codes - RFC 6585) */ public static final int SC_TOO_MANY_REQUESTS = 429; /** {@code 431 Request Header Fields Too Large} (Additional HTTP Status Codes - RFC 6585) */ public static final int SC_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; /** {@code 451 Unavailable For Legal Reasons} (Legal Obstacles - RFC 7725) */ public static final int SC_UNAVAILABLE_FOR_LEGAL_REASONS = 451; // --- 5xx Server Error --- /** {@code 500 Server Error} (HTTP/1.0 - RFC 7231) */ public static final int SC_SERVER_ERROR = 500; /** {@code 500 Internal Server Error} (HTTP/1.0 - RFC 7231) */ public static final int SC_INTERNAL_SERVER_ERROR = 500; /** {@code 501 Not Implemented} (HTTP/1.0 - RFC 7231) */ public static final int SC_NOT_IMPLEMENTED = 501; /** {@code 502 Bad Gateway} (HTTP/1.0 - RFC 7231) */ public static final int SC_BAD_GATEWAY = 502; /** {@code 503 Service Unavailable} (HTTP/1.0 - RFC 7231) */ public static final int SC_SERVICE_UNAVAILABLE = 503; /** {@code 504 Gateway Timeout} (HTTP/1.1 - RFC 7231) */ public static final int SC_GATEWAY_TIMEOUT = 504; /** {@code 505 HTTP Version Not Supported} (HTTP/1.1 - RFC 7231) */ public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505; /** {@code 506 Variant Also Negotiates} ( Transparent Content Negotiation - RFC 2295) */ public static final int SC_VARIANT_ALSO_NEGOTIATES = 506; /** {@code 507 Insufficient Storage} (WebDAV - RFC 2518) */ public static final int SC_INSUFFICIENT_STORAGE = 507; /** * {@code 508 Loop Detected} (WebDAV - RFC 5842, p.33, section 7.2) */ public static final int SC_LOOP_DETECTED = 508; /** * {@code 510 Not Extended} (An HTTP Extension Framework - RFC 2774, p. 10, section 7) */ public static final int SC_NOT_EXTENDED = 510; /** {@code 511 Network Authentication Required} (Additional HTTP Status Codes - RFC 6585) */ public static final int SC_NETWORK_AUTHENTICATION_REQUIRED = 511; } httpcore5/src/main/java/org/apache/hc/core5/http/HttpResponseInterceptor.java0100664 0000000 0000000 00000005646 14245617503 026320 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * HTTP protocol interceptor is a routine that implements a specific aspect of * the HTTP protocol. Usually protocol interceptors are expected to act upon * one specific header or a group of related headers of the incoming message * or populate the outgoing message with one specific header or a group of * related headers. Protocol *

* Interceptors can also manipulate content entities enclosed with messages. * Usually this is accomplished by using the 'Decorator' pattern where a wrapper * entity class is used to decorate the original entity. *

* Protocol interceptors must be implemented as thread-safe. Similarly to * servlets, protocol interceptors should not use instance variables unless * access to those variables is synchronized. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpResponseInterceptor { /** * Processes a response. * On the server side, this step is performed before the response is * sent to the client. On the client side, this step is performed * on incoming messages before the message body is evaluated. * * @param response the response to process * @param entity the request entity details or {@code null} if not available * @param context the context for the request * * @throws HttpException in case of an HTTP protocol violation * @throws IOException in case of an I/O error */ void process(HttpResponse response, EntityDetails entity, HttpContext context) throws HttpException, IOException; } httpcore5/src/main/java/org/apache/hc/core5/http/ClassicHttpRequest.java0100664 0000000 0000000 00000002611 14245617503 025222 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * 'Classic' {@link HttpRequest} message that can enclose {@link HttpEntity}. * * @since 5.0 */ public interface ClassicHttpRequest extends HttpRequest, HttpEntityContainer { // empty } httpcore5/src/main/java/org/apache/hc/core5/http/HttpEntity.java0100664 0000000 0000000 00000013360 14245617503 023547 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import org.apache.hc.core5.function.Supplier; /** * An entity that can be sent or received with an HTTP message. *

* There are three distinct types of entities in HttpCore, * depending on where their {@link #getContent content} originates: *

*
    *
  • streamed: The content is received from a stream, or * generated on the fly. In particular, this category includes * entities being received from a {@link HttpConnection connection}. * {@link #isStreaming Streamed} entities are generally not * {@link #isRepeatable repeatable}. *
  • *
  • self-contained: The content is in memory or obtained by * means that are independent from a connection or other entity. * Self-contained entities are generally {@link #isRepeatable repeatable}. *
  • *
  • wrapping: The content is obtained from another entity. *
  • *
*

* This distinction is important for connection management with incoming * entities. For entities that are created by an application and only sent * using the HTTP components framework, the difference between streamed * and self-contained is of little importance. In that case, it is suggested * to consider non-repeatable entities as streamed, and those that are * repeatable (without a huge effort) as self-contained. *

* * @since 4.0 */ public interface HttpEntity extends EntityDetails, Closeable { /** * Tells if the entity is capable of producing its data more than once. * A repeatable entity's getContent() and writeTo(OutputStream) methods * can be called more than once whereas a non-repeatable entity's can not. * @return true if the entity is repeatable, false otherwise. */ boolean isRepeatable(); /** * Returns a content stream of the entity. * {@link #isRepeatable Repeatable} entities are expected * to create a new instance of {@link InputStream} for each invocation * of this method and therefore can be consumed multiple times. * Entities that are not {@link #isRepeatable repeatable} are expected * to return the same {@link InputStream} instance and therefore * may not be consumed more than once. *

* If this entity belongs to an incoming HTTP message, calling * {@link InputStream#close()} on the returned {@code InputStream} will * try to consume the complete entity content to keep the connection * alive. In cases where this is undesired, e.g. when only a small part * of the content is relevant and consuming the complete entity content * would be too inefficient, only the HTTP message from which * this entity was obtained should be closed (if supported). *

*

* IMPORTANT: Please note all entity implementations must ensure that * all allocated resources are properly deallocated after * the {@link InputStream#close()} method is invoked. *

* @return content stream of the entity. * * @throws IOException if the stream could not be created * @throws UnsupportedOperationException * if entity content cannot be represented as {@link java.io.InputStream}. * * @see #isRepeatable() */ InputStream getContent() throws IOException, UnsupportedOperationException; /** * Writes the entity content out to the output stream. *

* IMPORTANT: Please note all entity implementations must ensure that * all allocated resources are properly deallocated when this method * returns. *

* * @param outStream the output stream to write entity content to * * @throws IOException if an I/O error occurs */ void writeTo(OutputStream outStream) throws IOException; /** * Tells whether this entity depends on an underlying stream. * Streamed entities that read data directly from the socket should * return {@code true}. Self-contained entities should return * {@code false}. Wrapping entities should delegate this call * to the wrapped entity. * * @return {@code true} if the entity content is streamed, * {@code false} otherwise */ boolean isStreaming(); // don't expect an exception here /** * Returns supplier of message trailers - headers sent after message body. * May return {@code null} if trailers are not available. * * @since 5.0 */ Supplier> getTrailers(); } httpcore5/src/main/java/org/apache/hc/core5/http/HttpRequestFactory.java0100664 0000000 0000000 00000004201 14245617503 025245 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.net.URI; /** * A factory for {@link HttpRequest} objects. * * @since 4.0 */ public interface HttpRequestFactory { /** * Creates request message with the given request method and request URI. * * @param method the request method * @param uri the request URI * * @return request message * @throws MethodNotSupportedException if the given {@code method} is not supported. * * @since 5.0 */ T newHttpRequest(String method, String uri) throws MethodNotSupportedException; /** * Creates request message with the given request method and request URI. * * @param method the request method * @param uri the request URI * * @return request message * @throws MethodNotSupportedException if the given {@code method} is not supported. */ T newHttpRequest(String method, URI uri) throws MethodNotSupportedException; } httpcore5/src/main/java/org/apache/hc/core5/http/impl/0040775 0000000 0000000 00000000000 14435411677 021536 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/impl/BasicEndpointDetails.java0100664 0000000 0000000 00000004550 14245617503 026425 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import java.net.SocketAddress; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.HttpConnectionMetrics; import org.apache.hc.core5.util.Timeout; /** * Basic HTTP connection endpoint details. * * @since 5.0 */ public final class BasicEndpointDetails extends EndpointDetails { private final HttpConnectionMetrics metrics; public BasicEndpointDetails( final SocketAddress remoteAddress, final SocketAddress localAddress, final HttpConnectionMetrics metrics, final Timeout socketTimeout) { super(remoteAddress, localAddress, socketTimeout); this.metrics = metrics; } @Override public long getRequestCount() { return metrics != null ? metrics.getRequestCount() : 0; } @Override public long getResponseCount() { return metrics != null ? metrics.getResponseCount() : 0; } @Override public long getSentBytesCount() { return metrics != null ? metrics.getSentBytesCount() : 0; } @Override public long getReceivedBytesCount() { return metrics != null ? metrics.getReceivedBytesCount() : 0; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/package-info.java0100664 0000000 0000000 00000002376 14245617503 024725 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Default implementations of core HTTP APIs. */ package org.apache.hc.core5.http.impl; httpcore5/src/main/java/org/apache/hc/core5/http/impl/BasicEntityDetails.java0100664 0000000 0000000 00000004136 14435411677 026126 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import java.util.Set; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.ContentType; /** * Basic HTTP message entity details. * * @since 5.0 */ public final class BasicEntityDetails implements EntityDetails { private final long len; private final ContentType contentType; public BasicEntityDetails(final long len, final ContentType contentType) { this.len = len; this.contentType = contentType; } @Override public long getContentLength() { return len; } @Override public String getContentType() { return contentType != null ? contentType.toString() : null; } @Override public String getContentEncoding() { return null; } @Override public boolean isChunked() { return false; } @Override public Set getTrailerNames() { return null; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/DefaultContentLengthStrategy.java0100664 0000000 0000000 00000010114 14245617503 030172 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.NotImplementedException; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.util.Args; /** * The default implementation of the content length strategy. This class * will throw {@link ProtocolException} if it encounters an unsupported * transfer encoding, multiple {@code Content-Length} header * values or a malformed {@code Content-Length} header value. *

* This class recognizes "chunked" transfer-coding only. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class DefaultContentLengthStrategy implements ContentLengthStrategy { public static final DefaultContentLengthStrategy INSTANCE = new DefaultContentLengthStrategy(); /** * Creates {@code DefaultContentLengthStrategy} instance. {@link ContentLengthStrategy#UNDEFINED} * is used per default when content length is not explicitly specified in the message. */ public DefaultContentLengthStrategy() { } @Override public long determineLength(final HttpMessage message) throws HttpException { Args.notNull(message, "HTTP message"); // Although Transfer-Encoding is specified as a list, in practice // it is either missing or has the single value "chunked". So we // treat it as a single-valued header here. final Header transferEncodingHeader = message.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); if (transferEncodingHeader != null) { final String headerValue = transferEncodingHeader.getValue(); if (HeaderElements.CHUNKED_ENCODING.equalsIgnoreCase(headerValue)) { return CHUNKED; } throw new NotImplementedException("Unsupported transfer encoding: " + headerValue); } if (message.countHeaders(HttpHeaders.CONTENT_LENGTH) > 1) { throw new ProtocolException("Multiple Content-Length headers"); } final Header contentLengthHeader = message.getFirstHeader(HttpHeaders.CONTENT_LENGTH); if (contentLengthHeader != null) { final String s = contentLengthHeader.getValue(); try { final long len = Long.parseLong(s); if (len < 0) { throw new ProtocolException("Negative content length: " + s); } return len; } catch (final NumberFormatException e) { throw new ProtocolException("Invalid content length: " + s); } } return UNDEFINED; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/Http1StreamListener.java0100664 0000000 0000000 00000003561 14245617503 026260 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; /** * HTTP/1.1 stream event listener. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public interface Http1StreamListener { void onRequestHead(HttpConnection connection, HttpRequest request); void onResponseHead(HttpConnection connection, HttpResponse response); void onExchangeComplete(HttpConnection connection, boolean keepAlive); } httpcore5/src/main/java/org/apache/hc/core5/http/impl/HttpProcessors.java0100664 0000000 0000000 00000012132 14403631147 025366 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.HttpProcessorBuilder; import org.apache.hc.core5.http.protocol.RequestConnControl; import org.apache.hc.core5.http.protocol.RequestContent; import org.apache.hc.core5.http.protocol.RequestExpectContinue; import org.apache.hc.core5.http.protocol.RequestTargetHost; import org.apache.hc.core5.http.protocol.RequestUserAgent; import org.apache.hc.core5.http.protocol.RequestValidateHost; import org.apache.hc.core5.http.protocol.ResponseConnControl; import org.apache.hc.core5.http.protocol.ResponseContent; import org.apache.hc.core5.http.protocol.ResponseDate; import org.apache.hc.core5.http.protocol.ResponseServer; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.VersionInfo; /** * Factory class for standard {@link HttpProcessor} instances. * * @since 5.0 */ public final class HttpProcessors { private final static String SOFTWARE = "Apache-HttpCore"; /** * Creates {@link HttpProcessorBuilder} initialized with default protocol interceptors * for server side HTTP/1.1 processing. * * @param serverInfo the server info text or {@code null} for default. * @return the processor builder. */ public static HttpProcessorBuilder customServer(final String serverInfo) { return HttpProcessorBuilder.create() .addAll( new ResponseDate(), new ResponseServer(!TextUtils.isBlank(serverInfo) ? serverInfo : VersionInfo.getSoftwareInfo(SOFTWARE, "org.apache.hc.core5", HttpProcessors.class)), new ResponseContent(), new ResponseConnControl()) .addAll( new RequestValidateHost()); } /** * Creates {@link HttpProcessor} initialized with default protocol interceptors * for server side HTTP/1.1 processing. * * @param serverInfo the server info text or {@code null} for default. * @return the processor. */ public static HttpProcessor server(final String serverInfo) { return customServer(serverInfo).build(); } /** * Creates {@link HttpProcessor} initialized with default protocol interceptors * for server side HTTP/1.1 processing. * * @return the processor. */ public static HttpProcessor server() { return customServer(null).build(); } /** * Creates {@link HttpProcessorBuilder} initialized with default protocol interceptors * for client side HTTP/1.1 processing. * * @param agentInfo the agent info text or {@code null} for default. * @return the processor builder. */ public static HttpProcessorBuilder customClient(final String agentInfo) { return HttpProcessorBuilder.create() .addAll( RequestContent.INSTANCE, RequestTargetHost.INSTANCE, RequestConnControl.INSTANCE, new RequestUserAgent(!TextUtils.isBlank(agentInfo) ? agentInfo : VersionInfo.getSoftwareInfo(SOFTWARE, "org.apache.hc.core5", HttpProcessors.class)), RequestExpectContinue.INSTANCE); } /** * Creates {@link HttpProcessor} initialized with default protocol interceptors * for client side HTTP/1.1 processing. * * @param agentInfo the agent info text or {@code null} for default. * @return the processor. */ public static HttpProcessor client(final String agentInfo) { return customClient(agentInfo).build(); } /** * Creates {@link HttpProcessor} initialized with default protocol interceptors * for client side HTTP/1.1 processing. * * @return the processor. */ public static HttpProcessor client() { return customClient(null).build(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/0040775 0000000 0000000 00000000000 14435411677 023553 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/package-info.java0100664 0000000 0000000 00000002406 14245617503 026734 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP/1.1 requester and server bootstrap. */ package org.apache.hc.core5.http.impl.bootstrap; httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/RequesterBootstrap.java0100664 0000000 0000000 00000020561 14403631147 030263 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultAddressResolver; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnectionFactory; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.ssl.DefaultTlsSetupHandler; import org.apache.hc.core5.http.io.ssl.SSLSessionVerifier; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.pool.ConnPoolListener; import org.apache.hc.core5.pool.DefaultDisposalCallback; import org.apache.hc.core5.pool.LaxConnPool; import org.apache.hc.core5.pool.ManagedConnPool; import org.apache.hc.core5.pool.PoolConcurrencyPolicy; import org.apache.hc.core5.pool.PoolReusePolicy; import org.apache.hc.core5.pool.StrictConnPool; import org.apache.hc.core5.util.Timeout; /** * {@link HttpRequester} bootstrap. * * @since 5.0 */ public class RequesterBootstrap { private HttpProcessor httpProcessor; private ConnectionReuseStrategy connReuseStrategy; private SocketConfig socketConfig; private HttpConnectionFactory connectFactory; private SSLSocketFactory sslSocketFactory; private Callback sslSetupHandler; private SSLSessionVerifier sslSessionVerifier; private int defaultMaxPerRoute; private int maxTotal; private Timeout timeToLive; private PoolReusePolicy poolReusePolicy; private PoolConcurrencyPolicy poolConcurrencyPolicy; private Http1StreamListener streamListener; private ConnPoolListener connPoolListener; private RequesterBootstrap() { } public static RequesterBootstrap bootstrap() { return new RequesterBootstrap(); } /** * Assigns {@link HttpProcessor} instance. */ public final RequesterBootstrap setHttpProcessor(final HttpProcessor httpProcessor) { this.httpProcessor = httpProcessor; return this; } /** * Assigns {@link ConnectionReuseStrategy} instance. */ public final RequesterBootstrap setConnectionReuseStrategy(final ConnectionReuseStrategy connStrategy) { this.connReuseStrategy = connStrategy; return this; } /** * Sets socket configuration. */ public final RequesterBootstrap setSocketConfig(final SocketConfig socketConfig) { this.socketConfig = socketConfig; return this; } public final RequesterBootstrap setConnectionFactory(final HttpConnectionFactory connectFactory) { this.connectFactory = connectFactory; return this; } public final RequesterBootstrap setSslContext(final SSLContext sslContext) { this.sslSocketFactory = sslContext != null ? sslContext.getSocketFactory() : null; return this; } public final RequesterBootstrap setSslSocketFactory(final SSLSocketFactory sslSocketFactory) { this.sslSocketFactory = sslSocketFactory; return this; } /** * Assigns {@link Callback} for {@link SSLParameters}. */ public final RequesterBootstrap setSslSetupHandler(final Callback sslSetupHandler) { this.sslSetupHandler = sslSetupHandler; return this; } /** * Assigns {@link SSLSessionVerifier} instance. */ public final RequesterBootstrap setSslSessionVerifier(final SSLSessionVerifier sslSessionVerifier) { this.sslSessionVerifier = sslSessionVerifier; return this; } public final RequesterBootstrap setDefaultMaxPerRoute(final int defaultMaxPerRoute) { this.defaultMaxPerRoute = defaultMaxPerRoute; return this; } public final RequesterBootstrap setMaxTotal(final int maxTotal) { this.maxTotal = maxTotal; return this; } public final RequesterBootstrap setTimeToLive(final Timeout timeToLive) { this.timeToLive = timeToLive; return this; } public final RequesterBootstrap setPoolReusePolicy(final PoolReusePolicy poolReusePolicy) { this.poolReusePolicy = poolReusePolicy; return this; } @Experimental public final RequesterBootstrap setPoolConcurrencyPolicy(final PoolConcurrencyPolicy poolConcurrencyPolicy) { this.poolConcurrencyPolicy = poolConcurrencyPolicy; return this; } public final RequesterBootstrap setStreamListener(final Http1StreamListener streamListener) { this.streamListener = streamListener; return this; } public final RequesterBootstrap setConnPoolListener(final ConnPoolListener connPoolListener) { this.connPoolListener = connPoolListener; return this; } public HttpRequester create() { final HttpRequestExecutor requestExecutor = new HttpRequestExecutor( HttpRequestExecutor.DEFAULT_WAIT_FOR_CONTINUE, connReuseStrategy != null ? connReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE, streamListener); final ManagedConnPool connPool; switch (poolConcurrencyPolicy != null ? poolConcurrencyPolicy : PoolConcurrencyPolicy.STRICT) { case LAX: connPool = new LaxConnPool<>( defaultMaxPerRoute > 0 ? defaultMaxPerRoute : 20, timeToLive, poolReusePolicy, new DefaultDisposalCallback<>(), connPoolListener); break; case STRICT: default: connPool = new StrictConnPool<>( defaultMaxPerRoute > 0 ? defaultMaxPerRoute : 20, maxTotal > 0 ? maxTotal : 50, timeToLive, poolReusePolicy, new DefaultDisposalCallback<>(), connPoolListener); break; } return new HttpRequester( requestExecutor, httpProcessor != null ? httpProcessor : HttpProcessors.client(), connPool, socketConfig != null ? socketConfig : SocketConfig.DEFAULT, connectFactory != null ? connectFactory : new DefaultBHttpClientConnectionFactory( Http1Config.DEFAULT, CharCodingConfig.DEFAULT), sslSocketFactory, sslSetupHandler != null ? sslSetupHandler : new DefaultTlsSetupHandler(), sslSessionVerifier, DefaultAddressResolver.INSTANCE); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServerBootstrap.java0100664 0000000 0000000 00000040555 14403631147 030555 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.config.NamedElementChain; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.nio.DefaultHttpRequestParserFactory; import org.apache.hc.core5.http.impl.nio.DefaultHttpResponseWriterFactory; import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandlerFactory; import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory; import org.apache.hc.core5.http.nio.AsyncFilterHandler; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.nio.support.AsyncServerExpectationFilter; import org.apache.hc.core5.http.nio.support.AsyncServerFilterChainElement; import org.apache.hc.core5.http.nio.support.AsyncServerFilterChainExchangeHandlerFactory; import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator; import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler; import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory; import org.apache.hc.core5.http.nio.support.TerminalAsyncServerFilter; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.LookupRegistry; import org.apache.hc.core5.http.protocol.RequestHandlerRegistry; import org.apache.hc.core5.http.protocol.UriPatternType; import org.apache.hc.core5.net.InetAddressUtils; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * {@link HttpAsyncServer} bootstrap. * * @since 5.0 */ public class AsyncServerBootstrap { private final List>> handlerList; private final List> filters; private String canonicalHostName; private LookupRegistry> lookupRegistry; private IOReactorConfig ioReactorConfig; private Http1Config http1Config; private CharCodingConfig charCodingConfig; private HttpProcessor httpProcessor; private ConnectionReuseStrategy connStrategy; private TlsStrategy tlsStrategy; private Timeout handshakeTimeout; private Decorator ioSessionDecorator; private Callback exceptionCallback; private IOSessionListener sessionListener; private Http1StreamListener streamListener; private AsyncServerBootstrap() { this.handlerList = new ArrayList<>(); this.filters = new ArrayList<>(); } public static AsyncServerBootstrap bootstrap() { return new AsyncServerBootstrap(); } /** * Sets canonical name (fully qualified domain name) of the server. */ public final AsyncServerBootstrap setCanonicalHostName(final String canonicalHostName) { this.canonicalHostName = canonicalHostName; return this; } /** * Sets I/O reactor configuration. */ public final AsyncServerBootstrap setIOReactorConfig(final IOReactorConfig ioReactorConfig) { this.ioReactorConfig = ioReactorConfig; return this; } /** * Sets HTTP/1.1 protocol parameters. */ public final AsyncServerBootstrap setHttp1Config(final Http1Config http1Config) { this.http1Config = http1Config; return this; } /** * Sets connection configuration. */ public final AsyncServerBootstrap setCharCodingConfig(final CharCodingConfig charCodingConfig) { this.charCodingConfig = charCodingConfig; return this; } /** * Assigns {@link org.apache.hc.core5.http.protocol.HttpProcessor} instance. */ public final AsyncServerBootstrap setHttpProcessor(final HttpProcessor httpProcessor) { this.httpProcessor = httpProcessor; return this; } /** * Assigns {@link org.apache.hc.core5.http.ConnectionReuseStrategy} instance. */ public final AsyncServerBootstrap setConnectionReuseStrategy(final ConnectionReuseStrategy connStrategy) { this.connStrategy = connStrategy; return this; } /** * Assigns {@link TlsStrategy} instance. */ public final AsyncServerBootstrap setTlsStrategy(final TlsStrategy tlsStrategy) { this.tlsStrategy = tlsStrategy; return this; } /** * Assigns TLS handshake {@link Timeout}. */ public final AsyncServerBootstrap setTlsHandshakeTimeout(final Timeout handshakeTimeout) { this.handshakeTimeout = handshakeTimeout; return this; } /** * Assigns {@link IOSession} {@link Decorator} instance. */ public final AsyncServerBootstrap setIOSessionDecorator(final Decorator ioSessionDecorator) { this.ioSessionDecorator = ioSessionDecorator; return this; } /** * Assigns {@link Exception} {@link Callback} instance. */ public final AsyncServerBootstrap setExceptionCallback(final Callback exceptionCallback) { this.exceptionCallback = exceptionCallback; return this; } /** * Assigns {@link IOSessionListener} instance. */ public final AsyncServerBootstrap setIOSessionListener(final IOSessionListener sessionListener) { this.sessionListener = sessionListener; return this; } /** * Assigns {@link LookupRegistry} instance. */ public final AsyncServerBootstrap setLookupRegistry(final LookupRegistry> lookupRegistry) { this.lookupRegistry = lookupRegistry; return this; } /** * Assigns {@link Http1StreamListener} instance. * * @since 5.0 */ public final AsyncServerBootstrap setStreamListener(final Http1StreamListener streamListener) { this.streamListener = streamListener; return this; } /** * Registers the given {@link AsyncServerExchangeHandler} {@link Supplier} as a default handler for URIs * matching the given pattern. * * @param uriPattern the pattern to register the handler for. * @param supplier the handler supplier. */ public final AsyncServerBootstrap register(final String uriPattern, final Supplier supplier) { Args.notBlank(uriPattern, "URI pattern"); Args.notNull(supplier, "Supplier"); handlerList.add(new HandlerEntry<>(null, uriPattern, supplier)); return this; } /** * Registers the given {@link AsyncServerExchangeHandler} {@link Supplier} as a handler for URIs * matching the given host and the pattern. * * @param hostname the host name * @param uriPattern the pattern to register the handler for. * @param supplier the handler supplier. */ public final AsyncServerBootstrap registerVirtual(final String hostname, final String uriPattern, final Supplier supplier) { Args.notBlank(hostname, "Hostname"); Args.notBlank(uriPattern, "URI pattern"); Args.notNull(supplier, "Supplier"); handlerList.add(new HandlerEntry<>(hostname, uriPattern, supplier)); return this; } /** * Registers the given {@link AsyncServerRequestHandler} as a default handler for URIs * matching the given pattern. * * @param uriPattern the pattern to register the handler for. * @param requestHandler the handler. */ public final AsyncServerBootstrap register( final String uriPattern, final AsyncServerRequestHandler requestHandler) { register(uriPattern, () -> new BasicServerExchangeHandler<>(requestHandler)); return this; } /** * Registers the given {@link AsyncServerRequestHandler} as a handler for URIs * matching the given host and the pattern. * * @param hostname the host name * @param uriPattern the pattern to register the handler for. * @param requestHandler the handler. */ public final AsyncServerBootstrap registerVirtual( final String hostname, final String uriPattern, final AsyncServerRequestHandler requestHandler) { registerVirtual(hostname, uriPattern, () -> new BasicServerExchangeHandler<>(requestHandler)); return this; } /** * Adds the filter before the filter with the given name. */ public final AsyncServerBootstrap addFilterBefore(final String existing, final String name, final AsyncFilterHandler filterHandler) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.BEFORE, name, filterHandler, existing)); return this; } /** * Adds the filter after the filter with the given name. */ public final AsyncServerBootstrap addFilterAfter(final String existing, final String name, final AsyncFilterHandler filterHandler) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.AFTER, name, filterHandler, existing)); return this; } /** * Replace an existing filter with the given name with new filter. */ public final AsyncServerBootstrap replaceFilter(final String existing, final AsyncFilterHandler filterHandler) { Args.notBlank(existing, "Existing"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.REPLACE, existing, filterHandler, existing)); return this; } /** * Add an filter to the head of the processing list. */ public final AsyncServerBootstrap addFilterFirst(final String name, final AsyncFilterHandler filterHandler) { Args.notNull(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.FIRST, name, filterHandler, null)); return this; } /** * Add an filter to the tail of the processing list. */ public final AsyncServerBootstrap addFilterLast(final String name, final AsyncFilterHandler filterHandler) { Args.notNull(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.LAST, name, filterHandler, null)); return this; } public HttpAsyncServer create() { final RequestHandlerRegistry> registry = new RequestHandlerRegistry<>( canonicalHostName != null ? canonicalHostName : InetAddressUtils.getCanonicalLocalHostName(), () -> lookupRegistry != null ? lookupRegistry : UriPatternType.newMatcher(UriPatternType.URI_PATTERN)); for (final HandlerEntry> entry: handlerList) { registry.register(entry.hostname, entry.uriPattern, entry.handler); } final HandlerFactory handlerFactory; if (!filters.isEmpty()) { final NamedElementChain filterChainDefinition = new NamedElementChain<>(); filterChainDefinition.addLast( new TerminalAsyncServerFilter(new DefaultAsyncResponseExchangeHandlerFactory(registry)), StandardFilter.MAIN_HANDLER.name()); filterChainDefinition.addFirst( new AsyncServerExpectationFilter(), StandardFilter.EXPECT_CONTINUE.name()); for (final FilterEntry entry: filters) { switch (entry.position) { case AFTER: filterChainDefinition.addAfter(entry.existing, entry.filterHandler, entry.name); break; case BEFORE: filterChainDefinition.addBefore(entry.existing, entry.filterHandler, entry.name); break; case REPLACE: filterChainDefinition.replace(entry.existing, entry.filterHandler); break; case FIRST: filterChainDefinition.addFirst(entry.filterHandler, entry.name); break; case LAST: // Don't add last, after TerminalAsyncServerFilter, as that does not delegate to the chain // Instead, add the filter just before it, making it effectively the last filter filterChainDefinition.addBefore(StandardFilter.MAIN_HANDLER.name(), entry.filterHandler, entry.name); break; } } NamedElementChain.Node current = filterChainDefinition.getLast(); AsyncServerFilterChainElement execChain = null; while (current != null) { execChain = new AsyncServerFilterChainElement(current.getValue(), execChain); current = current.getPrevious(); } handlerFactory = new AsyncServerFilterChainExchangeHandlerFactory(execChain, exceptionCallback); } else { handlerFactory = new DefaultAsyncResponseExchangeHandlerFactory(registry, handler -> new BasicAsyncServerExpectationDecorator(handler, exceptionCallback)); } final ServerHttp1StreamDuplexerFactory streamHandlerFactory = new ServerHttp1StreamDuplexerFactory( httpProcessor != null ? httpProcessor : HttpProcessors.server(), handlerFactory, http1Config != null ? http1Config : Http1Config.DEFAULT, charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT, connStrategy != null ? connStrategy : DefaultConnectionReuseStrategy.INSTANCE, DefaultHttpRequestParserFactory.INSTANCE, DefaultHttpResponseWriterFactory.INSTANCE, DefaultContentLengthStrategy.INSTANCE, DefaultContentLengthStrategy.INSTANCE, streamListener); final IOEventHandlerFactory ioEventHandlerFactory = new ServerHttp1IOEventHandlerFactory( streamHandlerFactory, tlsStrategy, handshakeTimeout); return new HttpAsyncServer(ioEventHandlerFactory, ioReactorConfig, ioSessionDecorator, exceptionCallback, sessionListener); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HttpAsyncServer.java0100664 0000000 0000000 00000011633 14245617503 027516 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.reactor.EndpointParameters; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.reactor.ListenerEndpoint; /** * HTTP/1.1 server side message exchange handler. * * @since 5.0 */ public class HttpAsyncServer extends AsyncServer { private final String canonicalName; /** * Use {@link AsyncServerBootstrap} to create instances of this class. * * @since 5.1 */ @Internal public HttpAsyncServer( final IOEventHandlerFactory eventHandlerFactory, final IOReactorConfig ioReactorConfig, final Decorator ioSessionDecorator, final Callback exceptionCallback, final IOSessionListener sessionListener, final String canonicalName) { super(eventHandlerFactory, ioReactorConfig, ioSessionDecorator, exceptionCallback, sessionListener, ShutdownCommand.GRACEFUL_NORMAL_CALLBACK); this.canonicalName = canonicalName; } /** * Use {@link AsyncServerBootstrap} to create instances of this class. */ @Internal public HttpAsyncServer( final IOEventHandlerFactory eventHandlerFactory, final IOReactorConfig ioReactorConfig, final Decorator ioSessionDecorator, final Callback exceptionCallback, final IOSessionListener sessionListener) { this(eventHandlerFactory, ioReactorConfig, ioSessionDecorator, exceptionCallback, sessionListener, null); } /** * @since 5.1 */ public Future listen( final SocketAddress address, final URIScheme scheme, final Object attachment, final FutureCallback callback) { final InetSocketAddress inetSocketAddress = (InetSocketAddress) address; final EndpointParameters parameters = new EndpointParameters( scheme.id, canonicalName != null ? canonicalName : "localhost", inetSocketAddress.getPort(), attachment); return super.listen(address, parameters, callback); } /** * @since 5.1 */ public Future listen( final SocketAddress address, final URIScheme scheme, final FutureCallback callback) { return listen(address, scheme, null, callback); } /** * @since 5.1 */ public Future listen(final SocketAddress address, final URIScheme scheme) { return listen(address, scheme, null, null); } /** * @deprecated Use {@link #listen(SocketAddress, URIScheme, FutureCallback)} */ @Deprecated @Override public Future listen(final SocketAddress address, final FutureCallback callback) { return super.listen(address, callback); } /** * @deprecated Use {@link #listen(SocketAddress, URIScheme)} */ @Deprecated @Override public Future listen(final SocketAddress address) { return super.listen(address); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/RequestListener.java0100664 0000000 0000000 00000010370 14245617503 027545 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.http.ExceptionListener; import org.apache.hc.core5.http.impl.io.HttpService; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.HttpServerConnection; import org.apache.hc.core5.http.io.SocketConfig; class RequestListener implements Runnable { private final SocketConfig socketConfig; private final ServerSocket serverSocket; private final HttpService httpService; private final HttpConnectionFactory connectionFactory; private final ExceptionListener exceptionListener; private final ExecutorService executorService; private final AtomicBoolean terminated; public RequestListener( final SocketConfig socketConfig, final ServerSocket serversocket, final HttpService httpService, final HttpConnectionFactory connectionFactory, final ExceptionListener exceptionListener, final ExecutorService executorService) { this.socketConfig = socketConfig; this.serverSocket = serversocket; this.connectionFactory = connectionFactory; this.httpService = httpService; this.exceptionListener = exceptionListener; this.executorService = executorService; this.terminated = new AtomicBoolean(false); } @Override public void run() { try { while (!isTerminated() && !Thread.interrupted()) { final Socket socket = this.serverSocket.accept(); socket.setSoTimeout(this.socketConfig.getSoTimeout().toMillisecondsIntBound()); socket.setKeepAlive(this.socketConfig.isSoKeepAlive()); socket.setTcpNoDelay(this.socketConfig.isTcpNoDelay()); if (this.socketConfig.getRcvBufSize() > 0) { socket.setReceiveBufferSize(this.socketConfig.getRcvBufSize()); } if (this.socketConfig.getSndBufSize() > 0) { socket.setSendBufferSize(this.socketConfig.getSndBufSize()); } if (this.socketConfig.getSoLinger().toSeconds() >= 0) { socket.setSoLinger(true, this.socketConfig.getSoLinger().toSecondsIntBound()); } final HttpServerConnection conn = this.connectionFactory.createConnection(socket); final Worker worker = new Worker(this.httpService, conn, this.exceptionListener); this.executorService.execute(worker); } } catch (final Exception ex) { this.exceptionListener.onError(ex); } } public boolean isTerminated() { return this.terminated.get(); } public void terminate() throws IOException { if (this.terminated.compareAndSet(false, true)) { this.serverSocket.close(); } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HttpRequester.java0100664 0000000 0000000 00000050272 14435411677 027240 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.Socket; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ConnectionRequestTimeoutException; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultAddressResolver; import org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnectionFactory; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.io.EofSensorInputStream; import org.apache.hc.core5.http.io.EofSensorWatcher; import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.HttpClientResponseHandler; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.HttpResponseInformationCallback; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.HttpEntityWrapper; import org.apache.hc.core5.http.io.ssl.SSLSessionVerifier; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.pool.ManagedConnPool; import org.apache.hc.core5.pool.PoolEntry; import org.apache.hc.core5.pool.PoolStats; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * HTTP/1.1 client side message exchange initiator. * * @since 5.0 */ public class HttpRequester implements ConnPoolControl, ModalCloseable { private final HttpRequestExecutor requestExecutor; private final HttpProcessor httpProcessor; private final ManagedConnPool connPool; private final SocketConfig socketConfig; private final HttpConnectionFactory connectFactory; private final SSLSocketFactory sslSocketFactory; private final Callback sslSetupHandler; private final SSLSessionVerifier sslSessionVerifier; private final Resolver addressResolver; /** * Use {@link RequesterBootstrap} to create instances of this class. */ @Internal public HttpRequester( final HttpRequestExecutor requestExecutor, final HttpProcessor httpProcessor, final ManagedConnPool connPool, final SocketConfig socketConfig, final HttpConnectionFactory connectFactory, final SSLSocketFactory sslSocketFactory, final Callback sslSetupHandler, final SSLSessionVerifier sslSessionVerifier, final Resolver addressResolver) { this.requestExecutor = Args.notNull(requestExecutor, "Request executor"); this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.connPool = Args.notNull(connPool, "Connection pool"); this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT; this.connectFactory = connectFactory != null ? connectFactory : new DefaultBHttpClientConnectionFactory( Http1Config.DEFAULT, CharCodingConfig.DEFAULT); this.sslSocketFactory = sslSocketFactory != null ? sslSocketFactory : (SSLSocketFactory) SSLSocketFactory.getDefault(); this.sslSetupHandler = sslSetupHandler; this.sslSessionVerifier = sslSessionVerifier; this.addressResolver = addressResolver != null ? addressResolver : DefaultAddressResolver.INSTANCE; } @Override public PoolStats getTotalStats() { return connPool.getTotalStats(); } @Override public PoolStats getStats(final HttpHost route) { return connPool.getStats(route); } @Override public void setMaxTotal(final int max) { connPool.setMaxTotal(max); } @Override public int getMaxTotal() { return connPool.getMaxTotal(); } @Override public void setDefaultMaxPerRoute(final int max) { connPool.setDefaultMaxPerRoute(max); } @Override public int getDefaultMaxPerRoute() { return connPool.getDefaultMaxPerRoute(); } @Override public void setMaxPerRoute(final HttpHost route, final int max) { connPool.setMaxPerRoute(route, max); } @Override public int getMaxPerRoute(final HttpHost route) { return connPool.getMaxPerRoute(route); } @Override public void closeIdle(final TimeValue idleTime) { connPool.closeIdle(idleTime); } @Override public void closeExpired() { connPool.closeExpired(); } @Override public Set getRoutes() { return connPool.getRoutes(); } public ClassicHttpResponse execute( final HttpClientConnection connection, final ClassicHttpRequest request, final HttpResponseInformationCallback informationCallback, final HttpContext context) throws HttpException, IOException { Args.notNull(connection, "HTTP connection"); Args.notNull(request, "HTTP request"); Args.notNull(context, "HTTP context"); if (!connection.isOpen()) { throw new ConnectionClosedException(); } requestExecutor.preProcess(request, httpProcessor, context); final ClassicHttpResponse response = requestExecutor.execute(request, connection, informationCallback, context); requestExecutor.postProcess(response, httpProcessor, context); return response; } public ClassicHttpResponse execute( final HttpClientConnection connection, final ClassicHttpRequest request, final HttpContext context) throws HttpException, IOException { return execute(connection, request, null, context); } public boolean keepAlive( final HttpClientConnection connection, final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws IOException { final boolean keepAlive = requestExecutor.keepAlive(request, response, connection, context); if (!keepAlive) { connection.close(); } return keepAlive; } public T execute( final HttpClientConnection connection, final ClassicHttpRequest request, final HttpContext context, final HttpClientResponseHandler responseHandler) throws HttpException, IOException { try (final ClassicHttpResponse response = execute(connection, request, context)) { final T result = responseHandler.handleResponse(response); EntityUtils.consume(response.getEntity()); final boolean keepAlive = requestExecutor.keepAlive(request, response, connection, context); if (!keepAlive) { connection.close(); } return result; } catch (final HttpException | IOException | RuntimeException ex) { connection.close(CloseMode.IMMEDIATE); throw ex; } } private Socket createSocket(final HttpHost targetHost) throws IOException { final Socket sock; if (socketConfig.getSocksProxyAddress() != null) { sock = new Socket(new Proxy(Proxy.Type.SOCKS, socketConfig.getSocksProxyAddress())); } else { sock = new Socket(); } sock.setSoTimeout(socketConfig.getSoTimeout().toMillisecondsIntBound()); sock.setReuseAddress(socketConfig.isSoReuseAddress()); sock.setTcpNoDelay(socketConfig.isTcpNoDelay()); sock.setKeepAlive(socketConfig.isSoKeepAlive()); if (socketConfig.getRcvBufSize() > 0) { sock.setReceiveBufferSize(socketConfig.getRcvBufSize()); } if (socketConfig.getSndBufSize() > 0) { sock.setSendBufferSize(socketConfig.getSndBufSize()); } final int linger = socketConfig.getSoLinger().toMillisecondsIntBound(); if (linger >= 0) { sock.setSoLinger(true, linger); } final InetSocketAddress targetAddress = addressResolver.resolve(targetHost); // Run this under a doPrivileged to support lib users that run under a SecurityManager this allows granting connect permissions // only to this library try { AccessController.doPrivileged((PrivilegedExceptionAction) () -> { sock.connect(targetAddress, socketConfig.getSoTimeout().toMillisecondsIntBound()); return null; }); } catch (final PrivilegedActionException e) { Asserts.check(e.getCause() instanceof IOException, "method contract violation only checked exceptions are wrapped: " + e.getCause()); // only checked exceptions are wrapped - error and RTExceptions are rethrown by doPrivileged throw (IOException) e.getCause(); } if (URIScheme.HTTPS.same(targetHost.getSchemeName())) { final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket( sock, targetHost.getHostName(), targetAddress.getPort(), true); if (this.sslSetupHandler != null) { final SSLParameters sslParameters = sslSocket.getSSLParameters(); this.sslSetupHandler.execute(sslParameters); sslSocket.setSSLParameters(sslParameters); } try { sslSocket.startHandshake(); final SSLSession session = sslSocket.getSession(); if (session == null) { throw new SSLHandshakeException("SSL session not available"); } if (sslSessionVerifier != null) { sslSessionVerifier.verify(targetHost, session); } } catch (final IOException ex) { Closer.closeQuietly(sslSocket); throw ex; } return sslSocket; } return sock; } public ClassicHttpResponse execute( final HttpHost targetHost, final ClassicHttpRequest request, final HttpResponseInformationCallback informationCallback, final Timeout connectTimeout, final HttpContext context) throws HttpException, IOException { Args.notNull(targetHost, "HTTP host"); Args.notNull(request, "HTTP request"); final Future> leaseFuture = connPool.lease(targetHost, null, connectTimeout, null); final PoolEntry poolEntry; final Timeout timeout = Timeout.defaultsToDisabled(connectTimeout); try { poolEntry = leaseFuture.get(timeout.getDuration(), timeout.getTimeUnit()); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); throw new InterruptedIOException(ex.getMessage()); } catch (final ExecutionException ex) { throw new HttpException("Unexpected failure leasing connection", ex); } catch (final TimeoutException ex) { throw new ConnectionRequestTimeoutException("Connection request timeout"); } final PoolEntryHolder connectionHolder = new PoolEntryHolder(poolEntry); try { HttpClientConnection connection = poolEntry.getConnection(); if (connection == null) { final Socket socket = createSocket(targetHost); connection = connectFactory.createConnection(socket); poolEntry.assignConnection(connection); } if (request.getAuthority() == null) { request.setAuthority(new URIAuthority(targetHost.getHostName(), targetHost.getPort())); } final ClassicHttpResponse response = execute(connection, request, informationCallback, context); final HttpEntity entity = response.getEntity(); if (entity != null) { response.setEntity(new HttpEntityWrapper(entity) { private void releaseConnection() throws IOException { try { final HttpClientConnection localConn = connectionHolder.getConnection(); if (localConn != null) { if (requestExecutor.keepAlive(request, response, localConn, context)) { if (super.isStreaming()) { Closer.close(super.getContent()); } connectionHolder.releaseConnection(); } } } finally { connectionHolder.discardConnection(); } } private void abortConnection() { connectionHolder.discardConnection(); } @Override public boolean isStreaming() { return true; } @Override public InputStream getContent() throws IOException { return new EofSensorInputStream(super.getContent(), new EofSensorWatcher() { @Override public boolean eofDetected(final InputStream wrapped) throws IOException { releaseConnection(); return false; } @Override public boolean streamClosed(final InputStream wrapped) throws IOException { releaseConnection(); return false; } @Override public boolean streamAbort(final InputStream wrapped) throws IOException { abortConnection(); return false; } }); } @Override public void writeTo(final OutputStream outStream) throws IOException { try { if (outStream != null) { super.writeTo(outStream); } close(); } catch (final IOException | RuntimeException ex) { abortConnection(); } } @Override public void close() throws IOException { releaseConnection(); } }); } else { final HttpClientConnection localConn = connectionHolder.getConnection(); if (!requestExecutor.keepAlive(request, response, localConn, context)) { localConn.close(); } connectionHolder.releaseConnection(); } return response; } catch (final HttpException | IOException | RuntimeException ex) { connectionHolder.discardConnection(); throw ex; } } public ClassicHttpResponse execute( final HttpHost targetHost, final ClassicHttpRequest request, final Timeout connectTimeout, final HttpContext context) throws HttpException, IOException { return execute(targetHost, request, null, connectTimeout, context); } public T execute( final HttpHost targetHost, final ClassicHttpRequest request, final Timeout connectTimeout, final HttpContext context, final HttpClientResponseHandler responseHandler) throws HttpException, IOException { try (final ClassicHttpResponse response = execute(targetHost, request, null, connectTimeout, context)) { final T result = responseHandler.handleResponse(response); EntityUtils.consume(response.getEntity()); return result; } } public ConnPoolControl getConnPoolControl() { return connPool; } @Override public void close(final CloseMode closeMode) { connPool.close(closeMode); } @Override public void close() throws IOException { connPool.close(); } private class PoolEntryHolder { private final AtomicReference> poolEntryRef; PoolEntryHolder(final PoolEntry poolEntry) { this.poolEntryRef = new AtomicReference<>(poolEntry); } HttpClientConnection getConnection() { final PoolEntry poolEntry = poolEntryRef.get(); return poolEntry != null ? poolEntry.getConnection() : null; } void releaseConnection() { final PoolEntry poolEntry = poolEntryRef.getAndSet(null); if (poolEntry != null) { final HttpClientConnection connection = poolEntry.getConnection(); connPool.release(poolEntry, connection != null && connection.isOpen()); } } void discardConnection() { final PoolEntry poolEntry = poolEntryRef.getAndSet(null); if (poolEntry != null) { poolEntry.discardConnection(CloseMode.GRACEFUL); connPool.release(poolEntry, false); } } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HandlerEntry.java0100664 0000000 0000000 00000003643 14245617503 027013 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; final class HandlerEntry { final String hostname; final String uriPattern; final T handler; public HandlerEntry(final String hostname, final String uriPattern, final T handler) { this.hostname = hostname; this.uriPattern = uriPattern; this.handler = handler; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("HandlerEntry [hostname="); builder.append(hostname); builder.append(", uriPattern="); builder.append(uriPattern); builder.append(", handler="); builder.append(handler); builder.append("]"); return builder.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncRequesterBootstrap.java0100664 0000000 0000000 00000022523 14403631147 031261 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandlerFactory; import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory; import org.apache.hc.core5.http.nio.ssl.BasicClientTlsStrategy; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.pool.ConnPoolListener; import org.apache.hc.core5.pool.DefaultDisposalCallback; import org.apache.hc.core5.pool.LaxConnPool; import org.apache.hc.core5.pool.ManagedConnPool; import org.apache.hc.core5.pool.PoolConcurrencyPolicy; import org.apache.hc.core5.pool.PoolReusePolicy; import org.apache.hc.core5.pool.StrictConnPool; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.util.Timeout; /** * {@link HttpAsyncRequester} bootstrap. * * @since 5.0 */ public class AsyncRequesterBootstrap { private IOReactorConfig ioReactorConfig; private Http1Config http1Config; private CharCodingConfig charCodingConfig; private HttpProcessor httpProcessor; private ConnectionReuseStrategy connStrategy; private int defaultMaxPerRoute; private int maxTotal; private Timeout timeToLive; private PoolReusePolicy poolReusePolicy; private PoolConcurrencyPolicy poolConcurrencyPolicy; private TlsStrategy tlsStrategy; private Timeout handshakeTimeout; private Decorator ioSessionDecorator; private Callback exceptionCallback; private IOSessionListener sessionListener; private Http1StreamListener streamListener; private ConnPoolListener connPoolListener; private AsyncRequesterBootstrap() { } public static AsyncRequesterBootstrap bootstrap() { return new AsyncRequesterBootstrap(); } /** * Sets I/O reactor configuration. */ public final AsyncRequesterBootstrap setIOReactorConfig(final IOReactorConfig ioReactorConfig) { this.ioReactorConfig = ioReactorConfig; return this; } /** * Sets HTTP/1.1 protocol parameters */ public final AsyncRequesterBootstrap setHttp1Config(final Http1Config http1Config) { this.http1Config = http1Config; return this; } /** * Sets message char coding. */ public final AsyncRequesterBootstrap setCharCodingConfig(final CharCodingConfig charCodingConfig) { this.charCodingConfig = charCodingConfig; return this; } /** * Assigns {@link HttpProcessor} instance. */ public final AsyncRequesterBootstrap setHttpProcessor(final HttpProcessor httpProcessor) { this.httpProcessor = httpProcessor; return this; } /** * Assigns {@link ConnectionReuseStrategy} instance. */ public final AsyncRequesterBootstrap setConnectionReuseStrategy(final ConnectionReuseStrategy connStrategy) { this.connStrategy = connStrategy; return this; } public final AsyncRequesterBootstrap setDefaultMaxPerRoute(final int defaultMaxPerRoute) { this.defaultMaxPerRoute = defaultMaxPerRoute; return this; } public final AsyncRequesterBootstrap setMaxTotal(final int maxTotal) { this.maxTotal = maxTotal; return this; } public final AsyncRequesterBootstrap setTimeToLive(final Timeout timeToLive) { this.timeToLive = timeToLive; return this; } /** * Assigns {@link PoolReusePolicy} instance. */ public final AsyncRequesterBootstrap setPoolReusePolicy(final PoolReusePolicy poolReusePolicy) { this.poolReusePolicy = poolReusePolicy; return this; } /** * Assigns {@link PoolConcurrencyPolicy} instance. */ @Experimental public final AsyncRequesterBootstrap setPoolConcurrencyPolicy(final PoolConcurrencyPolicy poolConcurrencyPolicy) { this.poolConcurrencyPolicy = poolConcurrencyPolicy; return this; } /** * Assigns {@link TlsStrategy} instance. */ public final AsyncRequesterBootstrap setTlsStrategy(final TlsStrategy tlsStrategy) { this.tlsStrategy = tlsStrategy; return this; } public final AsyncRequesterBootstrap setTlsHandshakeTimeout(final Timeout handshakeTimeout) { this.handshakeTimeout = handshakeTimeout; return this; } /** * Assigns {@link IOSession} {@link Decorator} instance. */ public final AsyncRequesterBootstrap setIOSessionDecorator(final Decorator ioSessionDecorator) { this.ioSessionDecorator = ioSessionDecorator; return this; } /** * Assigns {@link Exception} {@link Callback} instance. */ public final AsyncRequesterBootstrap setExceptionCallback(final Callback exceptionCallback) { this.exceptionCallback = exceptionCallback; return this; } /** * Assigns {@link IOSessionListener} instance. */ public final AsyncRequesterBootstrap setIOSessionListener(final IOSessionListener sessionListener) { this.sessionListener = sessionListener; return this; } /** * Assigns {@link Http1StreamListener} instance. */ public final AsyncRequesterBootstrap setStreamListener(final Http1StreamListener streamListener) { this.streamListener = streamListener; return this; } /** * Assigns {@link ConnPoolListener} instance. */ public final AsyncRequesterBootstrap setConnPoolListener(final ConnPoolListener connPoolListener) { this.connPoolListener = connPoolListener; return this; } public HttpAsyncRequester create() { final ManagedConnPool connPool; switch (poolConcurrencyPolicy != null ? poolConcurrencyPolicy : PoolConcurrencyPolicy.STRICT) { case LAX: connPool = new LaxConnPool<>( defaultMaxPerRoute > 0 ? defaultMaxPerRoute : 20, timeToLive, poolReusePolicy, new DefaultDisposalCallback<>(), connPoolListener); break; case STRICT: default: connPool = new StrictConnPool<>( defaultMaxPerRoute > 0 ? defaultMaxPerRoute : 20, maxTotal > 0 ? maxTotal : 50, timeToLive, poolReusePolicy, new DefaultDisposalCallback<>(), connPoolListener); break; } final ClientHttp1StreamDuplexerFactory streamDuplexerFactory = new ClientHttp1StreamDuplexerFactory( httpProcessor != null ? httpProcessor : HttpProcessors.client(), http1Config != null ? http1Config : Http1Config.DEFAULT, charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT, connStrategy, null, null, streamListener); final TlsStrategy tlsStrategyCopy = tlsStrategy != null ? tlsStrategy : new BasicClientTlsStrategy(); final IOEventHandlerFactory ioEventHandlerFactory = new ClientHttp1IOEventHandlerFactory( streamDuplexerFactory, tlsStrategyCopy, handshakeTimeout); return new HttpAsyncRequester( ioReactorConfig, ioEventHandlerFactory, ioSessionDecorator, exceptionCallback, sessionListener, connPool, tlsStrategyCopy, handshakeTimeout); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/ServerBootstrap.java0100664 0000000 0000000 00000041035 14403631147 027551 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocketFactory; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.ExceptionListener; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.config.NamedElementChain; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnection; import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnectionFactory; import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory; import org.apache.hc.core5.http.impl.io.HttpService; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.HttpFilterHandler; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.HttpServerRequestHandler; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.ssl.DefaultTlsSetupHandler; import org.apache.hc.core5.http.io.support.BasicHttpServerExpectationDecorator; import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler; import org.apache.hc.core5.http.io.support.HttpServerExpectationFilter; import org.apache.hc.core5.http.io.support.HttpServerFilterChainElement; import org.apache.hc.core5.http.io.support.HttpServerFilterChainRequestHandler; import org.apache.hc.core5.http.io.support.TerminalServerFilter; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.LookupRegistry; import org.apache.hc.core5.http.protocol.RequestHandlerRegistry; import org.apache.hc.core5.http.protocol.UriPatternType; import org.apache.hc.core5.net.InetAddressUtils; import org.apache.hc.core5.util.Args; /** * {@link HttpServer} bootstrap. * * @since 4.4 */ public class ServerBootstrap { private final List> handlerList; private final List> filters; private String canonicalHostName; private LookupRegistry lookupRegistry; private int listenerPort; private InetAddress localAddress; private SocketConfig socketConfig; private Http1Config http1Config; private CharCodingConfig charCodingConfig; private HttpProcessor httpProcessor; private ConnectionReuseStrategy connStrategy; private HttpResponseFactory responseFactory; private ServerSocketFactory serverSocketFactory; private SSLContext sslContext; private Callback sslSetupHandler; private HttpConnectionFactory connectionFactory; private ExceptionListener exceptionListener; private Http1StreamListener streamListener; private ServerBootstrap() { this.handlerList = new ArrayList<>(); this.filters = new ArrayList<>(); } public static ServerBootstrap bootstrap() { return new ServerBootstrap(); } /** * Sets canonical name (fully qualified domain name) of the server. * * @since 5.0 */ public final ServerBootstrap setCanonicalHostName(final String canonicalHostName) { this.canonicalHostName = canonicalHostName; return this; } /** * Sets listener port number. */ public final ServerBootstrap setListenerPort(final int listenerPort) { this.listenerPort = listenerPort; return this; } /** * Assigns local interface for the listener. */ public final ServerBootstrap setLocalAddress(final InetAddress localAddress) { this.localAddress = localAddress; return this; } /** * Sets socket configuration. */ public final ServerBootstrap setSocketConfig(final SocketConfig socketConfig) { this.socketConfig = socketConfig; return this; } /** * Sets connection configuration. */ public final ServerBootstrap setHttp1Config(final Http1Config http1Config) { this.http1Config = http1Config; return this; } /** * Sets connection configuration. */ public final ServerBootstrap setCharCodingConfig(final CharCodingConfig charCodingConfig) { this.charCodingConfig = charCodingConfig; return this; } /** * Assigns {@link HttpProcessor} instance. */ public final ServerBootstrap setHttpProcessor(final HttpProcessor httpProcessor) { this.httpProcessor = httpProcessor; return this; } /** * Assigns {@link ConnectionReuseStrategy} instance. */ public final ServerBootstrap setConnectionReuseStrategy(final ConnectionReuseStrategy connStrategy) { this.connStrategy = connStrategy; return this; } /** * Assigns {@link HttpResponseFactory} instance. */ public final ServerBootstrap setResponseFactory(final HttpResponseFactory responseFactory) { this.responseFactory = responseFactory; return this; } /** * Assigns {@link LookupRegistry} instance. */ public final ServerBootstrap setLookupRegistry(final LookupRegistry lookupRegistry) { this.lookupRegistry = lookupRegistry; return this; } /** * Registers the given {@link HttpRequestHandler} as a default handler for URIs * matching the given pattern. * * @param uriPattern the pattern to register the handler for. * @param requestHandler the handler. */ public final ServerBootstrap register(final String uriPattern, final HttpRequestHandler requestHandler) { Args.notBlank(uriPattern, "URI pattern"); Args.notNull(requestHandler, "Supplier"); handlerList.add(new HandlerEntry<>(null, uriPattern, requestHandler)); return this; } /** * Registers the given {@link HttpRequestHandler} as a handler for URIs * matching the given host and the pattern. * * @param hostname * @param uriPattern the pattern to register the handler for. * @param requestHandler the handler. */ public final ServerBootstrap registerVirtual(final String hostname, final String uriPattern, final HttpRequestHandler requestHandler) { Args.notBlank(hostname, "Hostname"); Args.notBlank(uriPattern, "URI pattern"); Args.notNull(requestHandler, "Supplier"); handlerList.add(new HandlerEntry<>(hostname, uriPattern, requestHandler)); return this; } /** * Assigns {@link HttpConnectionFactory} instance. */ public final ServerBootstrap setConnectionFactory( final HttpConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; return this; } /** * Assigns {@link javax.net.ServerSocketFactory} instance. */ public final ServerBootstrap setServerSocketFactory(final ServerSocketFactory serverSocketFactory) { this.serverSocketFactory = serverSocketFactory; return this; } /** * Assigns {@link javax.net.ssl.SSLContext} instance. *

* Please note this value can be overridden by the {@link #setServerSocketFactory( * javax.net.ServerSocketFactory)} method. */ public final ServerBootstrap setSslContext(final SSLContext sslContext) { this.sslContext = sslContext; return this; } /** * Assigns {@link Callback} for {@link SSLParameters}. */ public final ServerBootstrap setSslSetupHandler(final Callback sslSetupHandler) { this.sslSetupHandler = sslSetupHandler; return this; } /** * Assigns {@link ExceptionListener} instance. */ public final ServerBootstrap setExceptionListener(final ExceptionListener exceptionListener) { this.exceptionListener = exceptionListener; return this; } /** * Assigns {@link ExceptionListener} instance. */ public final ServerBootstrap setStreamListener(final Http1StreamListener streamListener) { this.streamListener = streamListener; return this; } /** * Adds the filter before the filter with the given name. */ public final ServerBootstrap addFilterBefore(final String existing, final String name, final HttpFilterHandler filterHandler) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.BEFORE, name, filterHandler, existing)); return this; } /** * Adds the filter after the filter with the given name. */ public final ServerBootstrap addFilterAfter(final String existing, final String name, final HttpFilterHandler filterHandler) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.AFTER, name, filterHandler, existing)); return this; } /** * Replace an existing filter with the given name with new filter. */ public final ServerBootstrap replaceFilter(final String existing, final HttpFilterHandler filterHandler) { Args.notBlank(existing, "Existing"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.REPLACE, existing, filterHandler, existing)); return this; } /** * Add an filter to the head of the processing list. */ public final ServerBootstrap addFilterFirst(final String name, final HttpFilterHandler filterHandler) { Args.notNull(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.FIRST, name, filterHandler, null)); return this; } /** * Add an filter to the tail of the processing list. */ public final ServerBootstrap addFilterLast(final String name, final HttpFilterHandler filterHandler) { Args.notNull(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.LAST, name, filterHandler, null)); return this; } public HttpServer create() { final RequestHandlerRegistry handlerRegistry = new RequestHandlerRegistry<>( canonicalHostName != null ? canonicalHostName : InetAddressUtils.getCanonicalLocalHostName(), () -> lookupRegistry != null ? lookupRegistry : UriPatternType.newMatcher(UriPatternType.URI_PATTERN)); for (final HandlerEntry entry: handlerList) { handlerRegistry.register(entry.hostname, entry.uriPattern, entry.handler); } final HttpServerRequestHandler requestHandler; if (!filters.isEmpty()) { final NamedElementChain filterChainDefinition = new NamedElementChain<>(); filterChainDefinition.addLast( new TerminalServerFilter( handlerRegistry, this.responseFactory != null ? this.responseFactory : DefaultClassicHttpResponseFactory.INSTANCE), StandardFilter.MAIN_HANDLER.name()); filterChainDefinition.addFirst( new HttpServerExpectationFilter(), StandardFilter.EXPECT_CONTINUE.name()); for (final FilterEntry entry: filters) { switch (entry.position) { case AFTER: filterChainDefinition.addAfter(entry.existing, entry.filterHandler, entry.name); break; case BEFORE: filterChainDefinition.addBefore(entry.existing, entry.filterHandler, entry.name); break; case REPLACE: filterChainDefinition.replace(entry.existing, entry.filterHandler); break; case FIRST: filterChainDefinition.addFirst(entry.filterHandler, entry.name); break; case LAST: // Don't add last, after TerminalServerFilter, as that does not delegate to the chain // Instead, add the filter just before it, making it effectively the last filter filterChainDefinition.addBefore(StandardFilter.MAIN_HANDLER.name(), entry.filterHandler, entry.name); break; } } NamedElementChain.Node current = filterChainDefinition.getLast(); HttpServerFilterChainElement filterChain = null; while (current != null) { filterChain = new HttpServerFilterChainElement(current.getValue(), filterChain); current = current.getPrevious(); } requestHandler = new HttpServerFilterChainRequestHandler(filterChain); } else { requestHandler = new BasicHttpServerExpectationDecorator(new BasicHttpServerRequestHandler( handlerRegistry, this.responseFactory != null ? this.responseFactory : DefaultClassicHttpResponseFactory.INSTANCE)); } final HttpService httpService = new HttpService( this.httpProcessor != null ? this.httpProcessor : HttpProcessors.server(), requestHandler, this.connStrategy != null ? this.connStrategy : DefaultConnectionReuseStrategy.INSTANCE, this.streamListener); ServerSocketFactory serverSocketFactoryCopy = this.serverSocketFactory; if (serverSocketFactoryCopy == null) { if (this.sslContext != null) { serverSocketFactoryCopy = this.sslContext.getServerSocketFactory(); } else { serverSocketFactoryCopy = ServerSocketFactory.getDefault(); } } HttpConnectionFactory connectionFactoryCopy = this.connectionFactory; if (connectionFactoryCopy == null) { final String scheme = serverSocketFactoryCopy instanceof SSLServerSocketFactory ? URIScheme.HTTPS.id : URIScheme.HTTP.id; connectionFactoryCopy = new DefaultBHttpServerConnectionFactory(scheme, this.http1Config, this.charCodingConfig); } return new HttpServer( Math.max(this.listenerPort, 0), httpService, this.localAddress, this.socketConfig != null ? this.socketConfig : SocketConfig.DEFAULT, serverSocketFactoryCopy, connectionFactoryCopy, sslSetupHandler != null ? sslSetupHandler : new DefaultTlsSetupHandler(), this.exceptionListener != null ? this.exceptionListener : ExceptionListener.NO_OP); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/Worker.java0100664 0000000 0000000 00000005247 14245617503 025667 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import org.apache.hc.core5.http.ExceptionListener; import org.apache.hc.core5.http.impl.io.HttpService; import org.apache.hc.core5.http.io.HttpServerConnection; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.io.CloseMode; class Worker implements Runnable { private final HttpService httpservice; private final HttpServerConnection conn; private final ExceptionListener exceptionListener; Worker( final HttpService httpservice, final HttpServerConnection conn, final ExceptionListener exceptionListener) { super(); this.httpservice = httpservice; this.conn = conn; this.exceptionListener = exceptionListener; } public HttpServerConnection getConnection() { return this.conn; } @Override public void run() { try { final BasicHttpContext localContext = new BasicHttpContext(); final HttpCoreContext context = HttpCoreContext.adapt(localContext); while (!Thread.interrupted() && this.conn.isOpen()) { this.httpservice.handleRequest(this.conn, context); localContext.clear(); } this.conn.close(); } catch (final Exception ex) { this.exceptionListener.onError(this.conn, ex); } finally { this.conn.close(CloseMode.IMMEDIATE); } } } ././@LongLink0100644 0000000 0000000 00000000145 14245617503 011642 Lustar 0000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AbstractConnectionInitiatorBase.javahttpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AbstractConnectionInitiatorBase.java0100664 0000000 0000000 00000004120 14245617503 032644 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import java.net.SocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Timeout; abstract class AbstractConnectionInitiatorBase implements ConnectionInitiator { @Override public final Future connect( final NamedEndpoint remoteEndpoint, final SocketAddress remoteAddress, final SocketAddress localAddress, final Timeout timeout, final Object attachment, final FutureCallback callback) { return getIOReactor().connect(remoteEndpoint, remoteAddress, localAddress, timeout, attachment, callback); } abstract ConnectionInitiator getIOReactor(); } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServer.java0100664 0000000 0000000 00000011415 14403631147 026650 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import java.io.IOException; import java.net.SocketAddress; import java.util.Set; import java.util.concurrent.Future; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.ConnectionAcceptor; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.DefaultListeningIOReactor; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOReactorService; import org.apache.hc.core5.reactor.IOReactorStatus; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.util.TimeValue; /** * Protocol agnostic server side I/O session handler. */ public class AsyncServer extends AbstractConnectionInitiatorBase implements IOReactorService, ConnectionAcceptor { private final DefaultListeningIOReactor ioReactor; @Internal public AsyncServer( final IOEventHandlerFactory eventHandlerFactory, final IOReactorConfig ioReactorConfig, final Decorator ioSessionDecorator, final Callback exceptionCallback, final IOSessionListener sessionListener, final Callback sessionShutdownCallback) { this.ioReactor = new DefaultListeningIOReactor( eventHandlerFactory, ioReactorConfig, new DefaultThreadFactory("server-dispatch", true), new DefaultThreadFactory("server-listener", true), ioSessionDecorator, exceptionCallback, sessionListener, sessionShutdownCallback); } @Override public void start() { ioReactor.start(); } @Override ConnectionInitiator getIOReactor() { return ioReactor; } /** * @since 5.1 */ @Override public Future listen( final SocketAddress address, final Object attachment, final FutureCallback callback) { return ioReactor.listen(address, attachment, callback); } @Override public Future listen(final SocketAddress address, final FutureCallback callback) { return listen(address, null, callback); } public Future listen(final SocketAddress address) { return ioReactor.listen(address, null); } @Override public void pause() throws IOException { ioReactor.pause(); } @Override public void resume() throws IOException { ioReactor.resume(); } @Override public Set getEndpoints() { return ioReactor.getEndpoints(); } @Override public IOReactorStatus getStatus() { return ioReactor.getStatus(); } @Override public void initiateShutdown() { ioReactor.initiateShutdown(); } @Override public void awaitShutdown(final TimeValue waitTime) throws InterruptedException { ioReactor.awaitShutdown(waitTime); } @Override public void close(final CloseMode closeMode) { ioReactor.close(closeMode); } @Override public void close() throws IOException { ioReactor.close(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HttpServer.java0100664 0000000 0000000 00000021766 14403631147 026524 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.util.Set; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.ExceptionListener; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnection; import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnectionFactory; import org.apache.hc.core5.http.impl.io.HttpService; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * HTTP/1.1 server side message exchange handler. * * @since 4.4 */ public class HttpServer implements ModalCloseable { enum Status { READY, ACTIVE, STOPPING } private final int port; private final InetAddress ifAddress; private final SocketConfig socketConfig; private final ServerSocketFactory serverSocketFactory; private final HttpService httpService; private final HttpConnectionFactory connectionFactory; private final Callback sslSetupHandler; private final ExceptionListener exceptionListener; private final ThreadPoolExecutor listenerExecutorService; private final ThreadGroup workerThreads; private final WorkerPoolExecutor workerExecutorService; private final AtomicReference status; private volatile ServerSocket serverSocket; private volatile RequestListener requestListener; @Internal public HttpServer( final int port, final HttpService httpService, final InetAddress ifAddress, final SocketConfig socketConfig, final ServerSocketFactory serverSocketFactory, final HttpConnectionFactory connectionFactory, final Callback sslSetupHandler, final ExceptionListener exceptionListener) { this.port = Args.notNegative(port, "Port value is negative"); this.httpService = Args.notNull(httpService, "HTTP service"); this.ifAddress = ifAddress; this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT; this.serverSocketFactory = serverSocketFactory != null ? serverSocketFactory : ServerSocketFactory.getDefault(); this.connectionFactory = connectionFactory != null ? connectionFactory : new DefaultBHttpServerConnectionFactory( this.serverSocketFactory instanceof SSLServerSocketFactory ? URIScheme.HTTPS.id : URIScheme.HTTP.id, Http1Config.DEFAULT, CharCodingConfig.DEFAULT); this.sslSetupHandler = sslSetupHandler; this.exceptionListener = exceptionListener != null ? exceptionListener : ExceptionListener.NO_OP; this.listenerExecutorService = new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<>(), new DefaultThreadFactory("HTTP-listener-" + this.port)); this.workerThreads = new ThreadGroup("HTTP-workers"); this.workerExecutorService = new WorkerPoolExecutor( 0, Integer.MAX_VALUE, 1L, TimeUnit.SECONDS, new SynchronousQueue<>(), new DefaultThreadFactory("HTTP-worker", this.workerThreads, true)); this.status = new AtomicReference<>(Status.READY); } public InetAddress getInetAddress() { final ServerSocket localSocket = this.serverSocket; if (localSocket != null) { return localSocket.getInetAddress(); } return null; } public int getLocalPort() { final ServerSocket localSocket = this.serverSocket; if (localSocket != null) { return localSocket.getLocalPort(); } return -1; } public void start() throws IOException { if (this.status.compareAndSet(Status.READY, Status.ACTIVE)) { this.serverSocket = this.serverSocketFactory.createServerSocket( this.port, this.socketConfig.getBacklogSize(), this.ifAddress); this.serverSocket.setReuseAddress(this.socketConfig.isSoReuseAddress()); if (this.socketConfig.getRcvBufSize() > 0) { this.serverSocket.setReceiveBufferSize(this.socketConfig.getRcvBufSize()); } if (this.sslSetupHandler != null && this.serverSocket instanceof SSLServerSocket) { final SSLServerSocket sslServerSocket = (SSLServerSocket) this.serverSocket; final SSLParameters sslParameters = sslServerSocket.getSSLParameters(); this.sslSetupHandler.execute(sslParameters); sslServerSocket.setSSLParameters(sslParameters); } this.requestListener = new RequestListener( this.socketConfig, this.serverSocket, this.httpService, this.connectionFactory, this.exceptionListener, this.workerExecutorService); this.listenerExecutorService.execute(this.requestListener); } } public void stop() { if (this.status.compareAndSet(Status.ACTIVE, Status.STOPPING)) { this.listenerExecutorService.shutdownNow(); this.workerExecutorService.shutdown(); final RequestListener local = this.requestListener; if (local != null) { try { local.terminate(); } catch (final IOException ex) { this.exceptionListener.onError(ex); } } this.workerThreads.interrupt(); } } public void initiateShutdown() { stop(); } public void awaitTermination(final TimeValue waitTime) throws InterruptedException { Args.notNull(waitTime, "Wait time"); this.workerExecutorService.awaitTermination(waitTime.getDuration(), waitTime.getTimeUnit()); } @Override public void close(final CloseMode closeMode) { close(closeMode, Timeout.ofSeconds(5)); } /** * Closes this process or endpoint and releases any system resources associated * with it. If the endpoint or the process is already closed then invoking this * method has no effect. * * @param closeMode How to close the receiver. * @param timeout How long to wait for the HttpServer to close gracefully. * @since 5.2 */ public void close(final CloseMode closeMode, final Timeout timeout) { initiateShutdown(); if (closeMode == CloseMode.GRACEFUL) { try { awaitTermination(timeout); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); } } final Set workers = this.workerExecutorService.getWorkers(); for (final Worker worker: workers) { Closer.close(worker.getConnection(), CloseMode.GRACEFUL); } } @Override public void close() { close(CloseMode.GRACEFUL); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/WorkerPoolExecutor.java0100664 0000000 0000000 00000004743 14245617503 030240 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; class WorkerPoolExecutor extends ThreadPoolExecutor { private final Map workerSet; public WorkerPoolExecutor( final int corePoolSize, final int maximumPoolSize, final long keepAliveTime, final TimeUnit unit, final BlockingQueue workQueue, final ThreadFactory threadFactory) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); this.workerSet = new ConcurrentHashMap<>(); } @Override protected void beforeExecute(final Thread t, final Runnable r) { if (r instanceof Worker) { this.workerSet.put((Worker) r, Boolean.TRUE); } } @Override protected void afterExecute(final Runnable r, final Throwable t) { if (r instanceof Worker) { this.workerSet.remove(r); } } public Set getWorkers() { return new HashSet<>(this.workerSet.keySet()); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HttpAsyncRequester.java0100664 0000000 0000000 00000053357 14403631147 030234 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.concurrent.FutureContribution; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.impl.DefaultAddressResolver; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.nio.command.RequestExecutionCommand; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.nio.ssl.TlsUpgradeCapable; import org.apache.hc.core5.http.nio.support.BasicClientExchangeHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.pool.ManagedConnPool; import org.apache.hc.core5.pool.PoolEntry; import org.apache.hc.core5.pool.PoolStats; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.EndpointParameters; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * HTTP/1.1 client side message exchange initiator. * * @since 5.0 */ public class HttpAsyncRequester extends AsyncRequester implements ConnPoolControl { private final ManagedConnPool connPool; private final TlsStrategy tlsStrategy; private final Timeout handshakeTimeout; /** * Use {@link AsyncRequesterBootstrap} to create instances of this class. * * @since 5.2 */ @Internal public HttpAsyncRequester( final IOReactorConfig ioReactorConfig, final IOEventHandlerFactory eventHandlerFactory, final Decorator ioSessionDecorator, final Callback exceptionCallback, final IOSessionListener sessionListener, final ManagedConnPool connPool, final TlsStrategy tlsStrategy, final Timeout handshakeTimeout) { super(eventHandlerFactory, ioReactorConfig, ioSessionDecorator, exceptionCallback, sessionListener, ShutdownCommand.GRACEFUL_IMMEDIATE_CALLBACK, DefaultAddressResolver.INSTANCE); this.connPool = Args.notNull(connPool, "Connection pool"); this.tlsStrategy = tlsStrategy; this.handshakeTimeout = handshakeTimeout; } /** * Use {@link AsyncRequesterBootstrap} to create instances of this class. */ @Internal public HttpAsyncRequester( final IOReactorConfig ioReactorConfig, final IOEventHandlerFactory eventHandlerFactory, final Decorator ioSessionDecorator, final Callback exceptionCallback, final IOSessionListener sessionListener, final ManagedConnPool connPool) { this(ioReactorConfig, eventHandlerFactory, ioSessionDecorator, exceptionCallback, sessionListener, connPool, null, null); } @Override public PoolStats getTotalStats() { return connPool.getTotalStats(); } @Override public PoolStats getStats(final HttpHost route) { return connPool.getStats(route); } @Override public void setMaxTotal(final int max) { connPool.setMaxTotal(max); } @Override public int getMaxTotal() { return connPool.getMaxTotal(); } @Override public void setDefaultMaxPerRoute(final int max) { connPool.setDefaultMaxPerRoute(max); } @Override public int getDefaultMaxPerRoute() { return connPool.getDefaultMaxPerRoute(); } @Override public void setMaxPerRoute(final HttpHost route, final int max) { connPool.setMaxPerRoute(route, max); } @Override public int getMaxPerRoute(final HttpHost route) { return connPool.getMaxPerRoute(route); } @Override public void closeIdle(final TimeValue idleTime) { connPool.closeIdle(idleTime); } @Override public void closeExpired() { connPool.closeExpired(); } @Override public Set getRoutes() { return connPool.getRoutes(); } public Future connect( final HttpHost host, final Timeout timeout, final Object attachment, final FutureCallback callback) { return doConnect(host, timeout, attachment, callback); } protected Future doConnect( final HttpHost host, final Timeout timeout, final Object attachment, final FutureCallback callback) { Args.notNull(host, "Host"); Args.notNull(timeout, "Timeout"); final ComplexFuture resultFuture = new ComplexFuture<>(callback); final Future> leaseFuture = connPool.lease( host, null, timeout, new FutureCallback>() { @Override public void completed(final PoolEntry poolEntry) { final AsyncClientEndpoint endpoint = new InternalAsyncClientEndpoint(poolEntry); final IOSession ioSession = poolEntry.getConnection(); if (ioSession != null && !ioSession.isOpen()) { poolEntry.discardConnection(CloseMode.IMMEDIATE); } if (poolEntry.hasConnection()) { resultFuture.completed(endpoint); } else { final Future future = requestSession( host, timeout, new EndpointParameters(host, attachment), new FutureCallback() { @Override public void completed(final IOSession session) { session.setSocketTimeout(timeout); poolEntry.assignConnection(session); resultFuture.completed(endpoint); } @Override public void failed(final Exception cause) { try { resultFuture.failed(cause); } finally { endpoint.releaseAndDiscard(); } } @Override public void cancelled() { try { resultFuture.cancel(); } finally { endpoint.releaseAndDiscard(); } } }); resultFuture.setDependency(future); } } @Override public void failed(final Exception ex) { resultFuture.failed(ex); } @Override public void cancelled() { resultFuture.cancel(); } }); resultFuture.setDependency(leaseFuture); return resultFuture; } public Future connect(final HttpHost host, final Timeout timeout) { return connect(host, timeout, null, null); } public void execute( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final Timeout timeout, final HttpContext executeContext) { Args.notNull(exchangeHandler, "Exchange handler"); Args.notNull(timeout, "Timeout"); Args.notNull(executeContext, "Context"); try { exchangeHandler.produceRequest((request, entityDetails, requestContext) -> { final String scheme = request.getScheme(); final URIAuthority authority = request.getAuthority(); if (authority == null) { throw new ProtocolException("Request authority not specified"); } final HttpHost target = new HttpHost(scheme, authority); connect(target, timeout, null, new FutureCallback() { @Override public void completed(final AsyncClientEndpoint endpoint) { endpoint.execute(new AsyncClientExchangeHandler() { @Override public void releaseResources() { endpoint.releaseAndDiscard(); exchangeHandler.releaseResources(); } @Override public void failed(final Exception cause) { endpoint.releaseAndDiscard(); exchangeHandler.failed(cause); } @Override public void cancel() { endpoint.releaseAndDiscard(); exchangeHandler.cancel(); } @Override public void produceRequest(final RequestChannel channel, final HttpContext httpContext) throws HttpException, IOException { channel.sendRequest(request, entityDetails, httpContext); } @Override public int available() { return exchangeHandler.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { exchangeHandler.produce(channel); } @Override public void consumeInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { exchangeHandler.consumeInformation(response, httpContext); } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext httpContext) throws HttpException, IOException { if (entityDetails == null) { endpoint.releaseAndReuse(); } exchangeHandler.consumeResponse(response, entityDetails, httpContext); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { exchangeHandler.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { exchangeHandler.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { endpoint.releaseAndReuse(); exchangeHandler.streamEnd(trailers); } }, pushHandlerFactory, executeContext); } @Override public void failed(final Exception ex) { exchangeHandler.failed(ex); } @Override public void cancelled() { exchangeHandler.cancel(); } }); }, executeContext); } catch (final IOException | HttpException ex) { exchangeHandler.failed(ex); } } public void execute( final AsyncClientExchangeHandler exchangeHandler, final Timeout timeout, final HttpContext executeContext) { execute(exchangeHandler, null, timeout, executeContext); } public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HandlerFactory pushHandlerFactory, final Timeout timeout, final HttpContext context, final FutureCallback callback) { Args.notNull(requestProducer, "Request producer"); Args.notNull(responseConsumer, "Response consumer"); Args.notNull(timeout, "Timeout"); final BasicFuture future = new BasicFuture<>(callback); final AsyncClientExchangeHandler exchangeHandler = new BasicClientExchangeHandler<>( requestProducer, responseConsumer, new FutureContribution(future) { @Override public void completed(final T result) { future.completed(result); } }); execute(exchangeHandler, pushHandlerFactory, timeout, context != null ? context : HttpCoreContext.create()); return future; } public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final Timeout timeout, final HttpContext context, final FutureCallback callback) { return execute(requestProducer, responseConsumer, null, timeout, context, callback); } public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final Timeout timeout, final FutureCallback callback) { return execute(requestProducer, responseConsumer, null, timeout, null, callback); } protected void doTlsUpgrade( final ProtocolIOSession ioSession, final NamedEndpoint endpoint, final FutureCallback callback) { if (tlsStrategy != null) { tlsStrategy.upgrade(ioSession, endpoint, null, handshakeTimeout, new CallbackContribution(callback) { @Override public void completed(final TransportSecurityLayer transportSecurityLayer) { if (callback != null) { callback.completed(ioSession); } } }); } else { throw new IllegalStateException("TLS upgrade not supported"); } } private class InternalAsyncClientEndpoint extends AsyncClientEndpoint implements TlsUpgradeCapable { final AtomicReference> poolEntryRef; InternalAsyncClientEndpoint(final PoolEntry poolEntry) { this.poolEntryRef = new AtomicReference<>(poolEntry); } private IOSession getIOSession() { final PoolEntry poolEntry = poolEntryRef.get(); if (poolEntry == null) { throw new IllegalStateException("Endpoint has already been released"); } final IOSession ioSession = poolEntry.getConnection(); if (ioSession == null) { throw new IllegalStateException("I/O session is invalid"); } return ioSession; } @Override public void execute( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final HttpContext context) { final IOSession ioSession = getIOSession(); ioSession.enqueue(new RequestExecutionCommand(exchangeHandler, pushHandlerFactory, null, context), Command.Priority.NORMAL); if (!ioSession.isOpen()) { try { exchangeHandler.failed(new ConnectionClosedException()); } finally { exchangeHandler.releaseResources(); } } } @Override public boolean isConnected() { final PoolEntry poolEntry = poolEntryRef.get(); if (poolEntry != null) { final IOSession ioSession = poolEntry.getConnection(); if (ioSession == null || !ioSession.isOpen()) { return false; } final IOEventHandler handler = ioSession.getHandler(); return (handler instanceof HttpConnection) && ((HttpConnection) handler).isOpen(); } return false; } @Override public void releaseAndReuse() { final PoolEntry poolEntry = poolEntryRef.getAndSet(null); if (poolEntry != null) { final IOSession ioSession = poolEntry.getConnection(); connPool.release(poolEntry, ioSession != null && ioSession.isOpen()); } } @Override public void releaseAndDiscard() { final PoolEntry poolEntry = poolEntryRef.getAndSet(null); if (poolEntry != null) { poolEntry.discardConnection(CloseMode.GRACEFUL); connPool.release(poolEntry, false); } } @Override public void tlsUpgrade(final NamedEndpoint endpoint, final FutureCallback callback) { final IOSession ioSession = getIOSession(); if (ioSession instanceof ProtocolIOSession) { doTlsUpgrade((ProtocolIOSession) ioSession, endpoint, callback); } else { throw new IllegalStateException("TLS upgrade not supported"); } } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/FilterEntry.java0100664 0000000 0000000 00000003305 14403631147 026652 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; final class FilterEntry { enum Position {BEFORE, AFTER, REPLACE, FIRST, LAST} final Position position; final String name; final T filterHandler; final String existing; FilterEntry( final Position position, final String name, final T filterHandler, final String existing) { this.position = position; this.name = name; this.filterHandler = filterHandler; this.existing = existing; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/StandardFilter.java0100664 0000000 0000000 00000002626 14245617503 027322 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; /** * Filters supported by async and classic server implementations * * @see AsyncServerBootstrap * @see ServerBootstrap */ public enum StandardFilter { EXPECT_CONTINUE, MAIN_HANDLER }httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncRequester.java0100664 0000000 0000000 00000011071 14334430335 027356 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.bootstrap; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.impl.DefaultAddressResolver; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOReactorService; import org.apache.hc.core5.reactor.IOReactorStatus; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Protocol agnostic client side I/O session initiator. * * @since 5.0 */ public class AsyncRequester extends AbstractConnectionInitiatorBase implements IOReactorService { private final DefaultConnectingIOReactor ioReactor; private final Resolver addressResolver; @Internal public AsyncRequester( final IOEventHandlerFactory eventHandlerFactory, final IOReactorConfig ioReactorConfig, final Decorator ioSessionDecorator, final Callback exceptionCallback, final IOSessionListener sessionListener, final Callback sessionShutdownCallback, final Resolver addressResolver) { this.ioReactor = new DefaultConnectingIOReactor( eventHandlerFactory, ioReactorConfig, new DefaultThreadFactory("requester-dispatch", true), ioSessionDecorator, exceptionCallback, sessionListener, sessionShutdownCallback); this.addressResolver = addressResolver != null ? addressResolver : DefaultAddressResolver.INSTANCE; } @Override ConnectionInitiator getIOReactor() { return ioReactor; } public Future requestSession( final HttpHost host, final Timeout timeout, final Object attachment, final FutureCallback callback) { Args.notNull(host, "Host"); Args.notNull(timeout, "Timeout"); return connect(host, addressResolver.resolve(host), null, timeout, attachment, callback); } @Override public void start() { ioReactor.start(); } @Override public IOReactorStatus getStatus() { return ioReactor.getStatus(); } @Override public void initiateShutdown() { ioReactor.initiateShutdown(); } @Override public void awaitShutdown(final TimeValue waitTime) throws InterruptedException { ioReactor.awaitShutdown(waitTime); } @Override public void close(final CloseMode closeMode) { ioReactor.close(closeMode); } @Override public void close() throws IOException { ioReactor.close(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/IncomingEntityDetails.java0100664 0000000 0000000 00000005544 14245617503 026647 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import java.util.Collections; import java.util.Set; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.MessageHeaders; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.util.Args; /** * HTTP message entity details. * * @since 5.0 */ @Internal public class IncomingEntityDetails implements EntityDetails { private final MessageHeaders message; private final long contentLength; public IncomingEntityDetails(final MessageHeaders message, final long contentLength) { this.message = Args.notNull(message, "Message"); this.contentLength = contentLength; } public IncomingEntityDetails(final MessageHeaders message) { this(message, -1); } @Override public long getContentLength() { return contentLength; } @Override public String getContentType() { final Header h = message.getFirstHeader(HttpHeaders.CONTENT_TYPE); return h != null ? h.getValue() : null; } @Override public String getContentEncoding() { final Header h = message.getFirstHeader(HttpHeaders.CONTENT_ENCODING); return h != null ? h.getValue() : null; } @Override public boolean isChunked() { return contentLength < 0; } @Override public Set getTrailerNames() { final Header h = message.getFirstHeader(HttpHeaders.TRAILER); if (h == null) { return Collections.emptySet(); } return MessageSupport.parseTokens(h); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/0040775 0000000 0000000 00000000000 14403631147 022312 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpResponseFactory.java0100664 0000000 0000000 00000005531 14245617503 030455 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.ReasonPhraseCatalog; import org.apache.hc.core5.http.impl.EnglishReasonPhraseCatalog; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.util.Args; /** * Default factory for creating {@link HttpResponse} objects. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultHttpResponseFactory implements HttpResponseFactory { public static final DefaultHttpResponseFactory INSTANCE = new DefaultHttpResponseFactory(); private final ReasonPhraseCatalog reasonCatalog; /** * Creates a new response factory with the given catalog. * * @param catalog the catalog of reason phrases */ public DefaultHttpResponseFactory(final ReasonPhraseCatalog catalog) { this.reasonCatalog = Args.notNull(catalog, "Reason phrase catalog"); } /** * Creates a new response factory with the default catalog. * The default catalog is {@link EnglishReasonPhraseCatalog}. */ public DefaultHttpResponseFactory() { this(EnglishReasonPhraseCatalog.INSTANCE); } @Override public HttpResponse newHttpResponse(final int status, final String reasonPhrase) { return new BasicHttpResponse(status, reasonPhrase); } @Override public HttpResponse newHttpResponse(final int status) { return new BasicHttpResponse(status, this.reasonCatalog, null); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/package-info.java0100664 0000000 0000000 00000002473 14245617503 025510 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Default implementation of HTTP/1.1 transport based on * the asynchronous (non-blocking) I/O model. */ package org.apache.hc.core5.http.impl.nio; httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1IOEventHandlerFactory.java0100664 0000000 0000000 00000007112 14403631147 031422 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.reactor.EndpointParameters; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * {@link ServerHttp1IOEventHandler} factory. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class ServerHttp1IOEventHandlerFactory implements IOEventHandlerFactory { private final ServerHttp1StreamDuplexerFactory streamDuplexerFactory; private final TlsStrategy tlsStrategy; private final Timeout handshakeTimeout; public ServerHttp1IOEventHandlerFactory( final ServerHttp1StreamDuplexerFactory streamDuplexerFactory, final TlsStrategy tlsStrategy, final Timeout handshakeTimeout) { this.streamDuplexerFactory = Args.notNull(streamDuplexerFactory, "Stream duplexer factory"); this.tlsStrategy = tlsStrategy; this.handshakeTimeout = handshakeTimeout; } @Override public IOEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { String endpointScheme = URIScheme.HTTP.id; if (tlsStrategy != null) { if (attachment instanceof EndpointParameters) { final EndpointParameters params = (EndpointParameters) attachment; endpointScheme = params.getScheme(); if (URIScheme.HTTPS.same(endpointScheme)) { tlsStrategy.upgrade( ioSession, params, params.getAttachment(), handshakeTimeout, null); } } else { endpointScheme = URIScheme.HTTPS.id; tlsStrategy.upgrade( ioSession, null, attachment, handshakeTimeout, null); } } return new ServerHttp1IOEventHandler(streamDuplexerFactory.create(endpointScheme, ioSession)); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpRequestFactory.java0100664 0000000 0000000 00000004120 14245617503 030300 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.net.URI; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestFactory; import org.apache.hc.core5.http.message.BasicHttpRequest; /** * Default factory for creating {@link HttpRequest} objects. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class DefaultHttpRequestFactory implements HttpRequestFactory { public static final DefaultHttpRequestFactory INSTANCE = new DefaultHttpRequestFactory(); @Override public HttpRequest newHttpRequest(final String method, final URI uri) { return new BasicHttpRequest(method, uri); } @Override public HttpRequest newHttpRequest(final String method, final String uri) { return new BasicHttpRequest(method, uri); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpResponseWriterFactory.java0100664 0000000 0000000 00000004601 14245617503 031647 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.BasicLineFormatter; import org.apache.hc.core5.http.message.LineFormatter; import org.apache.hc.core5.http.nio.NHttpMessageWriter; import org.apache.hc.core5.http.nio.NHttpMessageWriterFactory; /** * Default factory for response message writers. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultHttpResponseWriterFactory implements NHttpMessageWriterFactory { public static final DefaultHttpResponseWriterFactory INSTANCE = new DefaultHttpResponseWriterFactory(); private final LineFormatter lineFormatter; public DefaultHttpResponseWriterFactory(final LineFormatter lineFormatter) { super(); this.lineFormatter = lineFormatter != null ? lineFormatter : BasicLineFormatter.INSTANCE; } public DefaultHttpResponseWriterFactory() { this(null); } @Override public NHttpMessageWriter create() { return new DefaultHttpResponseWriter<>(this.lineFormatter); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/SessionOutputBufferImpl.java0100664 0000000 0000000 00000017216 14403631147 030001 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import org.apache.hc.core5.http.Chars; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; class SessionOutputBufferImpl extends ExpandableBuffer implements SessionOutputBuffer { private static final byte[] CRLF = new byte[] {Chars.CR, Chars.LF}; private final CharsetEncoder charEncoder; private final int lineBufferSize; private CharBuffer charbuffer; /** * Creates SessionOutputBufferImpl instance. * * @param bufferSize input buffer size * @param lineBufferSize buffer size for line operations. Has effect only if * {@code charEncoder} is not {@code null}. * @param charEncoder charEncoder to be used for encoding HTTP protocol elements. * If {@code null} simple type cast will be used for char to byte conversion. * * @since 4.3 */ public SessionOutputBufferImpl( final int bufferSize, final int lineBufferSize, final CharsetEncoder charEncoder) { super(bufferSize); this.lineBufferSize = Args.positive(lineBufferSize, "Line buffer size"); this.charEncoder = charEncoder; } /** * @since 4.3 */ public SessionOutputBufferImpl( final int bufferSize, final int lineBufferSize, final Charset charset) { this(bufferSize, lineBufferSize, charset != null ? charset.newEncoder() : null); } /** * @since 4.3 */ public SessionOutputBufferImpl( final int bufferSize, final int lineBufferSize) { this(bufferSize, lineBufferSize, (CharsetEncoder) null); } /** * @since 4.3 */ public SessionOutputBufferImpl(final int bufferSize) { this(bufferSize, 256); } @Override public int length() { return super.length(); } @Override public boolean hasData() { return super.hasData(); } @Override public int capacity() { return super.capacity(); } @Override public int flush(final WritableByteChannel channel) throws IOException { Args.notNull(channel, "Channel"); setOutputMode(); return channel.write(buffer()); } @Override public void write(final ByteBuffer src) { if (src == null) { return; } setInputMode(); ensureAdjustedCapacity(buffer().position() + src.remaining()); buffer().put(src); } @Override public void write(final ReadableByteChannel src) throws IOException { if (src == null) { return; } setInputMode(); src.read(buffer()); } private void write(final byte[] b) { if (b == null) { return; } setInputMode(); final int off = 0; final int len = b.length; final int requiredCapacity = buffer().position() + len; ensureAdjustedCapacity(requiredCapacity); buffer().put(b, off, len); } private void writeCRLF() { write(CRLF); } @Override public void writeLine(final CharArrayBuffer lineBuffer) throws CharacterCodingException { if (lineBuffer == null) { return; } setInputMode(); // Do not bother if the buffer is empty if (lineBuffer.length() > 0 ) { if (this.charEncoder == null) { final int requiredCapacity = buffer().position() + lineBuffer.length(); ensureCapacity(requiredCapacity); if (buffer().hasArray()) { final byte[] b = buffer().array(); final int len = lineBuffer.length(); final int off = buffer().position(); final int arrayOffset = buffer().arrayOffset(); for (int i = 0; i < len; i++) { b[arrayOffset + off + i] = (byte) lineBuffer.charAt(i); } buffer().position(off + len); } else { for (int i = 0; i < lineBuffer.length(); i++) { buffer().put((byte) lineBuffer.charAt(i)); } } } else { if (this.charbuffer == null) { this.charbuffer = CharBuffer.allocate(this.lineBufferSize); } this.charEncoder.reset(); // transfer the string in small chunks int remaining = lineBuffer.length(); int offset = 0; while (remaining > 0) { int l = this.charbuffer.remaining(); boolean eol = false; if (remaining <= l) { l = remaining; // terminate the encoding process eol = true; } this.charbuffer.put(lineBuffer.array(), offset, l); this.charbuffer.flip(); boolean retry = true; while (retry) { final CoderResult result = this.charEncoder.encode(this.charbuffer, buffer(), eol); if (result.isError()) { result.throwException(); } if (result.isOverflow()) { expand(); } retry = !result.isUnderflow(); } this.charbuffer.compact(); offset += l; remaining -= l; } // flush the encoder boolean retry = true; while (retry) { final CoderResult result = this.charEncoder.flush(buffer()); if (result.isError()) { result.throwException(); } if (result.isOverflow()) { expand(); } retry = !result.isUnderflow(); } } } writeCRLF(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1IOEventHandler.java0100664 0000000 0000000 00000010451 14245617503 030373 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.net.SocketAddress; import java.nio.ByteBuffer; import javax.net.ssl.SSLSession; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; class AbstractHttp1IOEventHandler implements HttpConnectionEventHandler { final AbstractHttp1StreamDuplexer streamDuplexer; AbstractHttp1IOEventHandler(final AbstractHttp1StreamDuplexer streamDuplexer) { this.streamDuplexer = Args.notNull(streamDuplexer, "Stream multiplexer"); } @Override public void connected(final IOSession session) throws IOException { try { streamDuplexer.onConnect(); } catch (final HttpException ex) { streamDuplexer.onException(ex); } } @Override public void inputReady(final IOSession session, final ByteBuffer src) throws IOException { try { streamDuplexer.onInput(src); } catch (final HttpException ex) { streamDuplexer.onException(ex); } } @Override public void outputReady(final IOSession session) throws IOException { try { streamDuplexer.onOutput(); } catch (final HttpException ex) { streamDuplexer.onException(ex); } } @Override public void timeout(final IOSession session, final Timeout timeout) throws IOException { try { streamDuplexer.onTimeout(timeout); } catch (final HttpException ex) { streamDuplexer.onException(ex); } } @Override public void exception(final IOSession session, final Exception cause) { streamDuplexer.onException(cause); } @Override public void disconnected(final IOSession session) { streamDuplexer.onDisconnect(); } @Override public void close() throws IOException { streamDuplexer.close(); } @Override public void close(final CloseMode closeMode) { streamDuplexer.close(closeMode); } @Override public boolean isOpen() { return streamDuplexer.isOpen(); } @Override public void setSocketTimeout(final Timeout timeout) { streamDuplexer.setSocketTimeout(timeout); } @Override public SSLSession getSSLSession() { return streamDuplexer.getSSLSession(); } @Override public EndpointDetails getEndpointDetails() { return streamDuplexer.getEndpointDetails(); } @Override public Timeout getSocketTimeout() { return streamDuplexer.getSocketTimeout(); } @Override public ProtocolVersion getProtocolVersion() { return streamDuplexer.getProtocolVersion(); } @Override public SocketAddress getRemoteAddress() { return streamDuplexer.getRemoteAddress(); } @Override public SocketAddress getLocalAddress() { return streamDuplexer.getLocalAddress(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1IOEventHandler.java0100664 0000000 0000000 00000004553 14323605260 030046 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.net.InetAddressUtils; /** * {@link org.apache.hc.core5.reactor.IOEventHandler} that implements * client side HTTP/1.1 messaging protocol with full support for * duplexed message transmission and message pipelining. * * @since 5.0 */ public class ClientHttp1IOEventHandler extends AbstractHttp1IOEventHandler { public ClientHttp1IOEventHandler(final ClientHttp1StreamDuplexer streamDuplexer) { super(streamDuplexer); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); InetAddressUtils.formatAddress(buf, getLocalAddress()); buf.append("->"); InetAddressUtils.formatAddress(buf, getRemoteAddress()); buf.append(" ["); streamDuplexer.appendState(buf); buf.append("]"); return buf.toString(); } @Override public ProtocolVersion getProtocolVersion() { final ProtocolVersion protocolVersion = super.getProtocolVersion(); return protocolVersion != null ? protocolVersion : HttpVersion.HTTP_1_1; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java0100664 0000000 0000000 00000034754 14245617503 030217 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.LengthRequiredException; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.ContentDecoder; import org.apache.hc.core5.http.nio.ContentEncoder; import org.apache.hc.core5.http.nio.NHttpMessageParser; import org.apache.hc.core5.http.nio.NHttpMessageWriter; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.http.nio.command.RequestExecutionCommand; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.Timeout; /** * I/O event handler for events fired by {@link ProtocolIOSession} that implements * client side HTTP/1.1 messaging protocol with full support for * duplexed message transmission and message pipelining. * * @since 5.0 */ @Internal public class ClientHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer { private final HttpProcessor httpProcessor; private final ConnectionReuseStrategy connectionReuseStrategy; private final Http1Config http1Config; private final Http1StreamListener streamListener; private final Queue pipeline; private final Http1StreamChannel outputChannel; private volatile ClientHttp1StreamHandler outgoing; private volatile ClientHttp1StreamHandler incoming; public ClientHttp1StreamDuplexer( final ProtocolIOSession ioSession, final HttpProcessor httpProcessor, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ConnectionReuseStrategy connectionReuseStrategy, final NHttpMessageParser incomingMessageParser, final NHttpMessageWriter outgoingMessageWriter, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final Http1StreamListener streamListener) { super(ioSession, http1Config, charCodingConfig, incomingMessageParser, outgoingMessageWriter, incomingContentStrategy, outgoingContentStrategy); this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.connectionReuseStrategy = connectionReuseStrategy != null ? connectionReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE; this.streamListener = streamListener; this.pipeline = new ConcurrentLinkedQueue<>(); this.outputChannel = new Http1StreamChannel() { @Override public void close() { shutdownSession(CloseMode.IMMEDIATE); } @Override public void submit( final HttpRequest request, final boolean endStream, final FlushMode flushMode) throws HttpException, IOException { if (streamListener != null) { streamListener.onRequestHead(ClientHttp1StreamDuplexer.this, request); } commitMessageHead(request, endStream, flushMode); } @Override public void suspendOutput() throws IOException { suspendSessionOutput(); } @Override public void requestOutput() { requestSessionOutput(); } @Override public Timeout getSocketTimeout() { return getSessionTimeout(); } @Override public void setSocketTimeout(final Timeout timeout) { setSessionTimeout(timeout); } @Override public int write(final ByteBuffer src) throws IOException { return streamOutput(src); } @Override public void complete(final List trailers) throws IOException { endOutputStream(trailers); } @Override public boolean isCompleted() { return isOutputCompleted(); } @Override public boolean abortGracefully() throws IOException { final MessageDelineation messageDelineation = endOutputStream(null); return messageDelineation != MessageDelineation.MESSAGE_HEAD; } @Override public void activate() throws HttpException, IOException { } }; } @Override void terminate(final Exception exception) { if (incoming != null) { incoming.failed(exception); incoming.releaseResources(); incoming = null; } if (outgoing != null) { outgoing.failed(exception); outgoing.releaseResources(); outgoing = null; } for (;;) { final ClientHttp1StreamHandler handler = pipeline.poll(); if (handler != null) { handler.failed(exception); handler.releaseResources(); } else { break; } } } @Override void disconnected() { if (incoming != null) { if (!incoming.isCompleted()) { incoming.failed(new ConnectionClosedException()); } incoming.releaseResources(); incoming = null; } if (outgoing != null) { if (!outgoing.isCompleted()) { outgoing.failed(new ConnectionClosedException()); } outgoing.releaseResources(); outgoing = null; } for (;;) { final ClientHttp1StreamHandler handler = pipeline.poll(); if (handler != null) { handler.failed(new ConnectionClosedException()); handler.releaseResources(); } else { break; } } } @Override void updateInputMetrics(final HttpResponse response, final BasicHttpConnectionMetrics connMetrics) { if (response.getCode() >= HttpStatus.SC_OK) { connMetrics.incrementRequestCount(); } } @Override void updateOutputMetrics(final HttpRequest request, final BasicHttpConnectionMetrics connMetrics) { connMetrics.incrementRequestCount(); } @Override protected boolean handleIncomingMessage(final HttpResponse response) throws HttpException { if (incoming == null) { incoming = pipeline.poll(); } if (incoming == null) { throw new HttpException("Unexpected response"); } return MessageSupport.canResponseHaveBody(incoming.getRequestMethod(), response); } @Override protected ContentDecoder createContentDecoder( final long len, final ReadableByteChannel channel, final SessionInputBuffer buffer, final BasicHttpTransportMetrics metrics) throws HttpException { if (len >= 0) { return new LengthDelimitedDecoder(channel, buffer, metrics, len); } else if (len == ContentLengthStrategy.CHUNKED) { return new ChunkDecoder(channel, buffer, http1Config, metrics); } else { return new IdentityDecoder(channel, buffer, metrics); } } @Override protected boolean handleOutgoingMessage(final HttpRequest request) throws HttpException { return true; } @Override protected ContentEncoder createContentEncoder( final long len, final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics) throws HttpException { final int chunkSizeHint = http1Config.getChunkSizeHint() >= 0 ? http1Config.getChunkSizeHint() : 2048; if (len >= 0) { return new LengthDelimitedEncoder(channel, buffer, metrics, len, chunkSizeHint); } else if (len == ContentLengthStrategy.CHUNKED) { return new ChunkEncoder(channel, buffer, metrics, chunkSizeHint); } else { throw new LengthRequiredException(); } } @Override boolean inputIdle() { return incoming == null; } @Override boolean outputIdle() { return outgoing == null && pipeline.isEmpty(); } @Override void outputEnd() throws HttpException, IOException { if (outgoing != null) { if (outgoing.isCompleted()) { outgoing.releaseResources(); } outgoing = null; } } @Override void execute(final RequestExecutionCommand executionCommand) throws HttpException, IOException { final AsyncClientExchangeHandler exchangeHandler = executionCommand.getExchangeHandler(); final HttpCoreContext context = HttpCoreContext.adapt(executionCommand.getContext()); context.setAttribute(HttpCoreContext.SSL_SESSION, getSSLSession()); context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, getEndpointDetails()); final ClientHttp1StreamHandler handler = new ClientHttp1StreamHandler( outputChannel, httpProcessor, http1Config, connectionReuseStrategy, exchangeHandler, context); pipeline.add(handler); outgoing = handler; if (handler.isOutputReady()) { handler.produceOutput(); } } @Override boolean isOutputReady() { return outgoing != null && outgoing.isOutputReady(); } @Override void produceOutput() throws HttpException, IOException { if (outgoing != null) { outgoing.produceOutput(); } } @Override void consumeHeader(final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException { if (streamListener != null) { streamListener.onResponseHead(this, response); } Asserts.notNull(incoming, "Response stream handler"); incoming.consumeHeader(response, entityDetails); } @Override void consumeData(final ByteBuffer src) throws HttpException, IOException { Asserts.notNull(incoming, "Response stream handler"); incoming.consumeData(src); } @Override void updateCapacity(final CapacityChannel capacityChannel) throws HttpException, IOException { Asserts.notNull(incoming, "Response stream handler"); incoming.updateCapacity(capacityChannel); } @Override void dataEnd(final List trailers) throws HttpException, IOException { Asserts.notNull(incoming, "Response stream handler"); incoming.dataEnd(trailers); } @Override void inputEnd() throws HttpException, IOException { if (incoming != null && incoming.isResponseFinal()) { if (streamListener != null) { streamListener.onExchangeComplete(this, isOpen()); } if (incoming.isCompleted()) { incoming.releaseResources(); } incoming = null; } } @Override boolean handleTimeout() { return outgoing != null && outgoing.handleTimeout(); } @Override void appendState(final StringBuilder buf) { super.appendState(buf); super.appendState(buf); buf.append(", incoming=["); if (incoming != null) { incoming.appendState(buf); } buf.append("], outgoing=["); if (outgoing != null) { outgoing.appendState(buf); } buf.append("], pipeline="); buf.append(pipeline.size()); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append("["); appendState(buf); buf.append("]"); return buf.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentDecoder.java0100664 0000000 0000000 00000013404 14245617503 027544 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.ContentDecoder; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.apache.hc.core5.util.Args; /** * Abstract {@link ContentDecoder} that serves as a base for all content * decoder implementations. * * @since 4.0 */ public abstract class AbstractContentDecoder implements ContentDecoder { final ReadableByteChannel channel; final SessionInputBuffer buffer; final BasicHttpTransportMetrics metrics; protected boolean completed; /** * Creates an instance of this class. * * @param channel the source channel. * @param buffer the session input buffer that can be used to store * session data for intermediate processing. * @param metrics Transport metrics of the underlying HTTP transport. */ public AbstractContentDecoder( final ReadableByteChannel channel, final SessionInputBuffer buffer, final BasicHttpTransportMetrics metrics) { super(); Args.notNull(channel, "Channel"); Args.notNull(buffer, "Session input buffer"); Args.notNull(metrics, "Transport metrics"); this.buffer = buffer; this.channel = channel; this.metrics = metrics; } protected ReadableByteChannel channel() { return this.channel; } protected SessionInputBuffer buffer() { return this.buffer; } protected BasicHttpTransportMetrics metrics() { return this.metrics; } @Override public boolean isCompleted() { return this.completed; } /** * Sets the completed status of this decoder. Normally this is not necessary * (the decoder will automatically complete when the underlying channel * returns EOF). It is useful to mark the decoder as completed if you have * some other means to know all the necessary data has been read and want to * reuse the underlying connection for more messages. * * @param completed the completed status of this decoder. * @since 4.4.11 */ public void setCompleted(final boolean completed) { this.completed = completed; } /** * Sets the completed status of this decoder to true. Normally this is not necessary * (the decoder will automatically complete when the underlying channel * returns EOF). It is useful to mark the decoder as completed if you have * some other means to know all the necessary data has been read and want to * reuse the underlying connection for more messages. * * @since 4.4.11 */ protected void setCompleted() { this.completed = true; } /** * Reads from the channel to the destination. * * @param dst destination. * @return number of bytes transferred. * * @since 4.3 */ protected int readFromChannel(final ByteBuffer dst) throws IOException { final int bytesRead = this.channel.read(dst); if (bytesRead > 0) { this.metrics.incrementBytesTransferred(bytesRead); } return bytesRead; } /** * Reads from the channel to the session buffer. * @return number of bytes transferred. * * @since 4.3 */ protected int fillBufferFromChannel() throws IOException { final int bytesRead = this.buffer.fill(this.channel); if (bytesRead > 0) { this.metrics.incrementBytesTransferred(bytesRead); } return bytesRead; } /** * Reads from the channel to the destination. * * @param dst destination. * @param limit max number of bytes to transfer. * @return number of bytes transferred. * * @since 4.3 */ protected int readFromChannel(final ByteBuffer dst, final int limit) throws IOException { final int bytesRead; if (dst.remaining() > limit) { final int oldLimit = dst.limit(); final int newLimit = oldLimit - (dst.remaining() - limit); dst.limit(newLimit); bytesRead = this.channel.read(dst); dst.limit(oldLimit); } else { bytesRead = this.channel.read(dst); } if (bytesRead > 0) { this.metrics.incrementBytesTransferred(bytesRead); } return bytesRead; } @Override public List getTrailers() { return null; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java0100664 0000000 0000000 00000047431 14330470156 030237 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.ContentDecoder; import org.apache.hc.core5.http.nio.ContentEncoder; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.NHttpMessageParser; import org.apache.hc.core5.http.nio.NHttpMessageWriter; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.http.nio.command.RequestExecutionCommand; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.Timeout; /** * I/O event handler for events fired by {@link ProtocolIOSession} that implements * server side HTTP/1.1 messaging protocol with full support for * duplexed message transmission and message pipelining. * * @since 5.0 */ @Internal public class ServerHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer { private final String scheme; private final HttpProcessor httpProcessor; private final HandlerFactory exchangeHandlerFactory; private final Http1Config http1Config; private final ConnectionReuseStrategy connectionReuseStrategy; private final Http1StreamListener streamListener; private final Queue pipeline; private final Http1StreamChannel outputChannel; private volatile ServerHttp1StreamHandler outgoing; private volatile ServerHttp1StreamHandler incoming; public ServerHttp1StreamDuplexer( final ProtocolIOSession ioSession, final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final String scheme, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ConnectionReuseStrategy connectionReuseStrategy, final NHttpMessageParser incomingMessageParser, final NHttpMessageWriter outgoingMessageWriter, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final Http1StreamListener streamListener) { super(ioSession, http1Config, charCodingConfig, incomingMessageParser, outgoingMessageWriter, incomingContentStrategy, outgoingContentStrategy); this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.exchangeHandlerFactory = Args.notNull(exchangeHandlerFactory, "Exchange handler factory"); this.scheme = scheme; this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.connectionReuseStrategy = connectionReuseStrategy != null ? connectionReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE; this.streamListener = streamListener; this.pipeline = new ConcurrentLinkedQueue<>(); this.outputChannel = new Http1StreamChannel() { @Override public void close() { ServerHttp1StreamDuplexer.this.close(CloseMode.GRACEFUL); } @Override public void submit( final HttpResponse response, final boolean endStream, final FlushMode flushMode) throws HttpException, IOException { if (streamListener != null) { streamListener.onResponseHead(ServerHttp1StreamDuplexer.this, response); } commitMessageHead(response, endStream, flushMode); } @Override public void requestOutput() { requestSessionOutput(); } @Override public void suspendOutput() throws IOException { suspendSessionOutput(); } @Override public Timeout getSocketTimeout() { return getSessionTimeout(); } @Override public void setSocketTimeout(final Timeout timeout) { setSessionTimeout(timeout); } @Override public int write(final ByteBuffer src) throws IOException { return streamOutput(src); } @Override public void complete(final List trailers) throws IOException { endOutputStream(trailers); } @Override public boolean isCompleted() { return isOutputCompleted(); } @Override public boolean abortGracefully() throws IOException { final MessageDelineation messageDelineation = endOutputStream(null); return messageDelineation != MessageDelineation.MESSAGE_HEAD; } @Override public void activate() throws HttpException, IOException { // empty } @Override public String toString() { return "Http1StreamChannel[" + ServerHttp1StreamDuplexer.this + "]"; } }; } @Override void terminate(final Exception exception) { if (incoming != null) { incoming.failed(exception); incoming.releaseResources(); incoming = null; } if (outgoing != null) { outgoing.failed(exception); outgoing.releaseResources(); outgoing = null; } for (;;) { final ServerHttp1StreamHandler handler = pipeline.poll(); if (handler != null) { handler.failed(exception); handler.releaseResources(); } else { break; } } } @Override void disconnected() { if (incoming != null) { if (!incoming.isCompleted()) { incoming.failed(new ConnectionClosedException()); } incoming.releaseResources(); incoming = null; } if (outgoing != null) { if (!outgoing.isCompleted()) { outgoing.failed(new ConnectionClosedException()); } outgoing.releaseResources(); outgoing = null; } for (;;) { final ServerHttp1StreamHandler handler = pipeline.poll(); if (handler != null) { handler.failed(new ConnectionClosedException()); handler.releaseResources(); } else { break; } } } @Override void updateInputMetrics(final HttpRequest request, final BasicHttpConnectionMetrics connMetrics) { connMetrics.incrementRequestCount(); } @Override void updateOutputMetrics(final HttpResponse response, final BasicHttpConnectionMetrics connMetrics) { if (response.getCode() >= HttpStatus.SC_OK) { connMetrics.incrementResponseCount(); } } @Override protected boolean handleIncomingMessage(final HttpRequest request) throws HttpException { return true; } @Override protected ContentDecoder createContentDecoder( final long len, final ReadableByteChannel channel, final SessionInputBuffer buffer, final BasicHttpTransportMetrics metrics) throws HttpException { if (len >= 0) { return new LengthDelimitedDecoder(channel, buffer, metrics, len); } else if (len == ContentLengthStrategy.CHUNKED) { return new ChunkDecoder(channel, buffer, http1Config, metrics); } else { return null; } } @Override protected boolean handleOutgoingMessage(final HttpResponse response) throws HttpException { return true; } @Override protected ContentEncoder createContentEncoder( final long len, final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics) throws HttpException { final int chunkSizeHint = http1Config.getChunkSizeHint() >= 0 ? http1Config.getChunkSizeHint() : 2048; if (len >= 0) { return new LengthDelimitedEncoder(channel, buffer, metrics, len, chunkSizeHint); } else if (len == ContentLengthStrategy.CHUNKED) { return new ChunkEncoder(channel, buffer, metrics, chunkSizeHint); } else { return new IdentityEncoder(channel, buffer, metrics, chunkSizeHint); } } @Override boolean inputIdle() { return incoming == null; } @Override boolean outputIdle() { return outgoing == null && pipeline.isEmpty(); } @Override HttpRequest parseMessageHead(final boolean endOfStream) throws IOException, HttpException { try { return super.parseMessageHead(endOfStream); } catch (final HttpException ex) { terminateExchange(ex); return null; } } void terminateExchange(final HttpException ex) throws HttpException, IOException { suspendSessionInput(); final ServerHttp1StreamHandler streamHandler; final HttpCoreContext context = HttpCoreContext.create(); context.setAttribute(HttpCoreContext.SSL_SESSION, getSSLSession()); context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, getEndpointDetails()); if (outgoing == null) { streamHandler = new ServerHttp1StreamHandler( outputChannel, httpProcessor, connectionReuseStrategy, exchangeHandlerFactory, context); outgoing = streamHandler; } else { streamHandler = new ServerHttp1StreamHandler( new DelayedOutputChannel(outputChannel), httpProcessor, connectionReuseStrategy, exchangeHandlerFactory, context); pipeline.add(streamHandler); } streamHandler.terminateExchange(ex); incoming = null; } @Override void consumeHeader(final HttpRequest request, final EntityDetails entityDetails) throws HttpException, IOException { if (streamListener != null) { streamListener.onRequestHead(this, request); } final ServerHttp1StreamHandler streamHandler; final HttpCoreContext context = HttpCoreContext.create(); context.setAttribute(HttpCoreContext.SSL_SESSION, getSSLSession()); context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, getEndpointDetails()); if (outgoing == null) { streamHandler = new ServerHttp1StreamHandler( outputChannel, httpProcessor, connectionReuseStrategy, exchangeHandlerFactory, context); outgoing = streamHandler; } else { streamHandler = new ServerHttp1StreamHandler( new DelayedOutputChannel(outputChannel), httpProcessor, connectionReuseStrategy, exchangeHandlerFactory, context); pipeline.add(streamHandler); } request.setScheme(scheme); streamHandler.consumeHeader(request, entityDetails); incoming = streamHandler; } @Override void consumeData(final ByteBuffer src) throws HttpException, IOException { Asserts.notNull(incoming, "Request stream handler"); incoming.consumeData(src); } @Override void updateCapacity(final CapacityChannel capacityChannel) throws HttpException, IOException { Asserts.notNull(incoming, "Request stream handler"); incoming.updateCapacity(capacityChannel); } @Override void dataEnd(final List trailers) throws HttpException, IOException { Asserts.notNull(incoming, "Request stream handler"); incoming.dataEnd(trailers); } @Override void inputEnd() throws HttpException, IOException { if (incoming != null) { if (incoming.isCompleted()) { incoming.releaseResources(); } incoming = null; } if (isShuttingDown() && outputIdle() && inputIdle()) { shutdownSession(CloseMode.IMMEDIATE); } } @Override void execute(final RequestExecutionCommand executionCommand) throws HttpException { throw new HttpException("Illegal command: " + executionCommand.getClass()); } @Override boolean isOutputReady() { return outgoing != null && outgoing.isOutputReady(); } @Override void produceOutput() throws HttpException, IOException { if (outgoing != null) { outgoing.produceOutput(); } } @Override void outputEnd() throws HttpException, IOException { if (outgoing != null && outgoing.isResponseFinal()) { if (streamListener != null) { streamListener.onExchangeComplete(this, outgoing.keepAlive()); } if (outgoing.isCompleted()) { outgoing.releaseResources(); } outgoing = null; } if (outgoing == null && isActive()) { final ServerHttp1StreamHandler handler = pipeline.poll(); if (handler != null) { outgoing = handler; handler.activateChannel(); if (handler.isOutputReady()) { handler.produceOutput(); } } } if (isShuttingDown() && outputIdle() && inputIdle()) { shutdownSession(CloseMode.IMMEDIATE); } } @Override boolean handleTimeout() { return false; } @Override void appendState(final StringBuilder buf) { super.appendState(buf); buf.append(", incoming=["); if (incoming != null) { incoming.appendState(buf); } buf.append("], outgoing=["); if (outgoing != null) { outgoing.appendState(buf); } buf.append("], pipeline="); buf.append(pipeline.size()); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append("["); appendState(buf); buf.append("]"); return buf.toString(); } private static class DelayedOutputChannel implements Http1StreamChannel { private final Http1StreamChannel channel; private volatile boolean direct; private volatile HttpResponse delayedResponse; private volatile boolean completed; private DelayedOutputChannel(final Http1StreamChannel channel) { this.channel = channel; } @Override public void close() { channel.close(); } @Override public void submit( final HttpResponse response, final boolean endStream, final FlushMode flushMode) throws HttpException, IOException { synchronized (this) { if (direct) { channel.submit(response, endStream, flushMode); } else { delayedResponse = response; completed = endStream; } } } @Override public void suspendOutput() throws IOException { channel.suspendOutput(); } @Override public void requestOutput() { channel.requestOutput(); } @Override public Timeout getSocketTimeout() { return channel.getSocketTimeout(); } @Override public void setSocketTimeout(final Timeout timeout) { channel.setSocketTimeout(timeout); } @Override public int write(final ByteBuffer src) throws IOException { synchronized (this) { return direct ? channel.write(src) : 0; } } @Override public void complete(final List trailers) throws IOException { synchronized (this) { if (direct) { channel.complete(trailers); } else { completed = true; } } } @Override public boolean abortGracefully() throws IOException { synchronized (this) { if (direct) { return channel.abortGracefully(); } completed = true; return true; } } @Override public boolean isCompleted() { synchronized (this) { return direct ? channel.isCompleted() : completed; } } @Override public void activate() throws IOException, HttpException { synchronized (this) { direct = true; if (delayedResponse != null) { channel.submit(delayedResponse, completed, completed ? FlushMode.IMMEDIATE : FlushMode.BUFFER); delayedResponse = null; } } } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/BufferedData.java0100664 0000000 0000000 00000005755 14245617503 025506 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import org.apache.hc.core5.util.Args; /** * A buffer that expand its capacity on demand. Internally, this class is backed * by an instance of {@link ByteBuffer}. *

* This class is not thread safe. *

* @since 5.0 */ public class BufferedData extends ExpandableBuffer { public static BufferedData allocate(final int bufferSize) { return new BufferedData(bufferSize); } protected BufferedData(final int bufferSize) { super(bufferSize); } @Override public final boolean hasData() { return super.hasData(); } @Override public final int length() { return super.length(); } @Override public final int capacity() { return super.capacity(); } @Override public final void clear() { super.clear(); } public final void put(final ByteBuffer src) { Args.notNull(src, "Data source"); setInputMode(); final int requiredCapacity = buffer().position() + src.remaining(); ensureAdjustedCapacity(requiredCapacity); buffer().put(src); } public final int readFrom(final ReadableByteChannel channel) throws IOException { Args.notNull(channel, "Channel"); setInputMode(); if (!buffer().hasRemaining()) { expand(); } return channel.read(buffer()); } public final int writeTo(final WritableByteChannel dst) throws IOException { if (dst == null) { return 0; } setOutputMode(); return dst.write(buffer()); } public final ByteBuffer data() { setOutputMode(); return buffer(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/LengthDelimitedEncoder.java0100664 0000000 0000000 00000014701 14403631147 027517 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.WritableByteChannel; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.FileContentEncoder; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.util.Args; /** * Content encoder that cuts off after a defined number of bytes. This class * is used to send content of HTTP messages where the end of the content entity * is determined by the value of the {@code Content-Length header}. * Entities transferred using this stream can be maximum {@link Long#MAX_VALUE} * long. *

* This decoder is optimized to transfer data directly from * a {@link FileChannel} to the underlying I/O session's channel whenever * possible avoiding intermediate buffering in the session buffer. * * @since 4.0 */ public class LengthDelimitedEncoder extends AbstractContentEncoder implements FileContentEncoder { private final long contentLength; private final int fragHint; private long remaining; /** * @since 4.3 * * @param channel underlying channel. * @param buffer session buffer. * @param metrics transport metrics. * @param contentLength content length. * @param chunkSizeHint fragment size hint defining an minimal size of a fragment * that should be written out directly to the channel bypassing the session buffer. * Value {@code 0} disables fragment buffering. */ public LengthDelimitedEncoder( final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics, final long contentLength, final int chunkSizeHint) { super(channel, buffer, metrics); Args.notNegative(contentLength, "Content length"); this.contentLength = contentLength; this.fragHint = Math.max(chunkSizeHint, 0); this.remaining = contentLength; } public LengthDelimitedEncoder( final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics, final long contentLength) { this(channel, buffer, metrics, contentLength, 0); } private int nextChunk(final ByteBuffer src) { return (int) Math.min(Math.min(this.remaining, Integer.MAX_VALUE), src.remaining()); } @Override public int write(final ByteBuffer src) throws IOException { if (src == null) { return 0; } assertNotCompleted(); int total = 0; while (src.hasRemaining() && this.remaining > 0) { if (this.buffer.hasData() || this.fragHint > 0) { final int chunk = nextChunk(src); if (chunk <= this.fragHint) { final int capacity = this.fragHint - this.buffer.length(); if (capacity > 0) { final int limit = Math.min(capacity, chunk); final int bytesWritten = writeToBuffer(src, limit); this.remaining -= bytesWritten; total += bytesWritten; } } } if (this.buffer.hasData()) { final int chunk = nextChunk(src); if (this.buffer.length() >= this.fragHint || chunk > 0) { final int bytesWritten = flushToChannel(); if (bytesWritten == 0) { break; } } } if (!this.buffer.hasData()) { final int chunk = nextChunk(src); if (chunk > this.fragHint) { final int bytesWritten = writeToChannel(src, chunk); this.remaining -= bytesWritten; total += bytesWritten; if (bytesWritten == 0) { break; } } } } if (this.remaining <= 0) { super.complete(null); } return total; } @Override public long transfer( final FileChannel src, final long position, final long count) throws IOException { if (src == null) { return 0; } assertNotCompleted(); flushToChannel(); if (this.buffer.hasData()) { return 0; } final long chunk = Math.min(this.remaining, count); final long bytesWritten = src.transferTo(position, chunk, this.channel); if (bytesWritten > 0) { this.metrics.incrementBytesTransferred(bytesWritten); } this.remaining -= bytesWritten; if (this.remaining <= 0) { super.complete(null); } return bytesWritten; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("[content length: "); sb.append(this.contentLength); sb.append("; pos: "); sb.append(this.contentLength - this.remaining); sb.append("; completed: "); sb.append(isCompleted()); sb.append("]"); return sb.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/FlushMode.java0100664 0000000 0000000 00000002367 14245617503 025054 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; enum FlushMode { IMMEDIATE, BUFFER } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/SessionInputBufferImpl.java0100664 0000000 0000000 00000023717 14403631147 027603 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CoderResult; import org.apache.hc.core5.http.Chars; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; class SessionInputBufferImpl extends ExpandableBuffer implements SessionInputBuffer { private final CharsetDecoder charDecoder; private final int lineBuffersize; private final int maxLineLen; private CharBuffer charbuffer; /** * Creates SessionInputBufferImpl instance. * * @param bufferSize input buffer size * @param lineBuffersize buffer size for line operations. Has effect only if * {@code charDecoder} is not {@code null}. * @param charDecoder charDecoder to be used for decoding HTTP protocol elements. * If {@code null} simple type cast will be used for byte to char conversion. * @param maxLineLen maximum line length. * * @since 4.4 */ public SessionInputBufferImpl( final int bufferSize, final int lineBuffersize, final int maxLineLen, final CharsetDecoder charDecoder) { super(bufferSize); this.lineBuffersize = Args.positive(lineBuffersize, "Line buffer size"); this.maxLineLen = Math.max(maxLineLen, 0); this.charDecoder = charDecoder; } /** * @since 4.3 */ public SessionInputBufferImpl( final int bufferSize, final int lineBuffersize, final int maxLineLen, final Charset charset) { this(bufferSize, lineBuffersize, maxLineLen, charset != null ? charset.newDecoder() : null); } /** * @since 4.3 */ public SessionInputBufferImpl( final int bufferSize, final int lineBuffersize, final int maxLineLen) { this(bufferSize, lineBuffersize, maxLineLen, (CharsetDecoder) null); } /** * @since 4.3 */ public SessionInputBufferImpl( final int bufferSize, final int lineBuffersize) { this(bufferSize, lineBuffersize, 0, (CharsetDecoder) null); } /** * @since 4.3 */ public SessionInputBufferImpl(final int bufferSize) { this(bufferSize, 256); } @Override public int length() { return super.length(); } @Override public boolean hasData() { return super.hasData(); } @Override public int capacity() { return super.capacity(); } public void put(final ByteBuffer src) { if (src != null && src.hasRemaining()) { setInputMode(); ensureAdjustedCapacity(buffer().position() + src.remaining()); buffer().put(src); } } @Override public int fill(final ReadableByteChannel channel) throws IOException { Args.notNull(channel, "Channel"); setInputMode(); if (!buffer().hasRemaining()) { expand(); } return channel.read(buffer()); } @Override public int read() { setOutputMode(); return buffer().get() & 0xff; } @Override public int read(final ByteBuffer dst, final int maxLen) { if (dst == null) { return 0; } setOutputMode(); final int len = Math.min(dst.remaining(), maxLen); final int chunk = Math.min(buffer().remaining(), len); if (buffer().remaining() > chunk) { final int oldLimit = buffer().limit(); final int newLimit = buffer().position() + chunk; buffer().limit(newLimit); dst.put(buffer()); buffer().limit(oldLimit); return len; } dst.put(buffer()); return chunk; } @Override public int read(final ByteBuffer dst) { if (dst == null) { return 0; } return read(dst, dst.remaining()); } @Override public int read(final WritableByteChannel dst, final int maxLen) throws IOException { if (dst == null) { return 0; } setOutputMode(); final int bytesRead; if (buffer().remaining() > maxLen) { final int oldLimit = buffer().limit(); final int newLimit = oldLimit - (buffer().remaining() - maxLen); buffer().limit(newLimit); bytesRead = dst.write(buffer()); buffer().limit(oldLimit); } else { bytesRead = dst.write(buffer()); } return bytesRead; } @Override public int read(final WritableByteChannel dst) throws IOException { if (dst == null) { return 0; } setOutputMode(); return dst.write(buffer()); } @Override public boolean readLine( final CharArrayBuffer lineBuffer, final boolean endOfStream) throws IOException { setOutputMode(); // See if there is LF char present in the buffer int pos = -1; for (int i = buffer().position(); i < buffer().limit(); i++) { final int b = buffer().get(i); if (b == Chars.LF) { pos = i + 1; break; } } if (this.maxLineLen > 0) { final int currentLen = (pos > 0 ? pos : buffer().limit()) - buffer().position(); if (currentLen >= this.maxLineLen) { throw new MessageConstraintException("Maximum line length limit exceeded"); } } if (pos == -1) { if (endOfStream && buffer().hasRemaining()) { // No more data. Get the rest pos = buffer().limit(); } else { // Either no complete line present in the buffer // or no more data is expected return false; } } final int origLimit = buffer().limit(); buffer().limit(pos); final int requiredCapacity = buffer().limit() - buffer().position(); // Ensure capacity of len assuming ASCII as the most likely charset lineBuffer.ensureCapacity(requiredCapacity); if (this.charDecoder == null) { if (buffer().hasArray()) { final byte[] b = buffer().array(); final int off = buffer().position(); final int len = buffer().remaining(); lineBuffer.append(b, buffer().arrayOffset() + off, len); buffer().position(off + len); } else { while (buffer().hasRemaining()) { lineBuffer.append((char) (buffer().get() & 0xff)); } } } else { if (this.charbuffer == null) { this.charbuffer = CharBuffer.allocate(this.lineBuffersize); } this.charDecoder.reset(); for (;;) { final CoderResult result = this.charDecoder.decode( buffer(), this.charbuffer, true); if (result.isError()) { result.throwException(); } if (result.isOverflow()) { this.charbuffer.flip(); lineBuffer.append( this.charbuffer.array(), this.charbuffer.arrayOffset() + this.charbuffer.position(), this.charbuffer.remaining()); this.charbuffer.clear(); } if (result.isUnderflow()) { break; } } // flush the decoder this.charDecoder.flush(this.charbuffer); this.charbuffer.flip(); // append the decoded content to the line buffer if (this.charbuffer.hasRemaining()) { lineBuffer.append( this.charbuffer.array(), this.charbuffer.arrayOffset() + this.charbuffer.position(), this.charbuffer.remaining()); } } buffer().limit(origLimit); // discard LF if found int l = lineBuffer.length(); if (l > 0) { if (lineBuffer.charAt(l - 1) == Chars.LF) { l--; lineBuffer.setLength(l); } // discard CR if found if (l > 0 && lineBuffer.charAt(l - 1) == Chars.CR) { l--; lineBuffer.setLength(l); } } return true; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java0100664 0000000 0000000 00000034533 14245617503 030027 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.MisdirectedRequestException; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.UnsupportedHttpVersionException; import org.apache.hc.core5.http.impl.ServerSupport; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.AsyncResponseProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.ResourceHolder; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.nio.support.BasicResponseProducer; import org.apache.hc.core5.http.nio.support.ImmediateResponseExchangeHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; class ServerHttp1StreamHandler implements ResourceHolder { private final Http1StreamChannel outputChannel; private final DataStreamChannel internalDataChannel; private final ResponseChannel responseChannel; private final HttpProcessor httpProcessor; private final HandlerFactory exchangeHandlerFactory; private final ConnectionReuseStrategy connectionReuseStrategy; private final HttpCoreContext context; private final AtomicBoolean responseCommitted; private final AtomicBoolean done; private volatile boolean keepAlive; private volatile AsyncServerExchangeHandler exchangeHandler; private volatile HttpRequest receivedRequest; private volatile MessageState requestState; private volatile MessageState responseState; ServerHttp1StreamHandler( final Http1StreamChannel outputChannel, final HttpProcessor httpProcessor, final ConnectionReuseStrategy connectionReuseStrategy, final HandlerFactory exchangeHandlerFactory, final HttpCoreContext context) { this.outputChannel = outputChannel; this.internalDataChannel = new DataStreamChannel() { @Override public void requestOutput() { outputChannel.requestOutput(); } @Override public void endStream(final List trailers) throws IOException { outputChannel.complete(trailers); if (!keepAlive) { outputChannel.close(); } responseState = MessageState.COMPLETE; } @Override public int write(final ByteBuffer src) throws IOException { return outputChannel.write(src); } @Override public void endStream() throws IOException { endStream(null); } }; this.responseChannel = new ResponseChannel() { @Override public void sendInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { commitInformation(response); } @Override public void sendResponse( final HttpResponse response, final EntityDetails responseEntityDetails, final HttpContext httpContext) throws HttpException, IOException { ServerSupport.validateResponse(response, responseEntityDetails); commitResponse(response, responseEntityDetails); } @Override public void pushPromise( final HttpRequest promise, final AsyncPushProducer pushProducer, final HttpContext httpContext) throws HttpException, IOException { commitPromise(); } @Override public String toString() { return super.toString() + " " + ServerHttp1StreamHandler.this; } }; this.httpProcessor = httpProcessor; this.connectionReuseStrategy = connectionReuseStrategy; this.exchangeHandlerFactory = exchangeHandlerFactory; this.context = context; this.responseCommitted = new AtomicBoolean(false); this.done = new AtomicBoolean(false); this.keepAlive = true; this.requestState = MessageState.HEADERS; this.responseState = MessageState.IDLE; } private void commitResponse( final HttpResponse response, final EntityDetails responseEntityDetails) throws HttpException, IOException { if (responseCommitted.compareAndSet(false, true)) { final ProtocolVersion transportVersion = response.getVersion(); if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) { throw new UnsupportedHttpVersionException(transportVersion); } final int status = response.getCode(); if (status < HttpStatus.SC_SUCCESS) { throw new HttpException("Invalid response: " + status); } context.setProtocolVersion(transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1); context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); httpProcessor.process(response, responseEntityDetails, context); final boolean endStream = responseEntityDetails == null || (receivedRequest != null && Method.HEAD.isSame(receivedRequest.getMethod())); if (!connectionReuseStrategy.keepAlive(receivedRequest, response, context)) { keepAlive = false; } outputChannel.submit(response, endStream, endStream ? FlushMode.IMMEDIATE : FlushMode.BUFFER); if (endStream) { if (!keepAlive) { outputChannel.close(); } responseState = MessageState.COMPLETE; } else { responseState = MessageState.BODY; exchangeHandler.produce(internalDataChannel); } } else { throw new HttpException("Response already committed"); } } private void commitInformation(final HttpResponse response) throws IOException, HttpException { if (responseCommitted.get()) { throw new HttpException("Response already committed"); } final int status = response.getCode(); if (status < HttpStatus.SC_INFORMATIONAL || status >= HttpStatus.SC_SUCCESS) { throw new HttpException("Invalid intermediate response: " + status); } outputChannel.submit(response, true, FlushMode.IMMEDIATE); } private void commitPromise() throws HttpException { throw new HttpException("HTTP/1.1 does not support server push"); } void activateChannel() throws IOException, HttpException { outputChannel.activate(); } boolean isResponseFinal() { return responseState == MessageState.COMPLETE; } boolean keepAlive() { return keepAlive; } boolean isCompleted() { return requestState == MessageState.COMPLETE && responseState == MessageState.COMPLETE; } void terminateExchange(final HttpException ex) throws HttpException, IOException { if (done.get() || requestState != MessageState.HEADERS) { throw new ProtocolException("Unexpected message head"); } receivedRequest = null; requestState = MessageState.COMPLETE; final HttpResponse response = new BasicHttpResponse(ServerSupport.toStatusCode(ex)); response.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); final AsyncResponseProducer responseProducer = new BasicResponseProducer(response, ServerSupport.toErrorMessage(ex)); exchangeHandler = new ImmediateResponseExchangeHandler(responseProducer); exchangeHandler.handleRequest(null, null, responseChannel, context); } void consumeHeader(final HttpRequest request, final EntityDetails requestEntityDetails) throws HttpException, IOException { if (done.get() || requestState != MessageState.HEADERS) { throw new ProtocolException("Unexpected message head"); } receivedRequest = request; requestState = requestEntityDetails == null ? MessageState.COMPLETE : MessageState.BODY; AsyncServerExchangeHandler handler; try { handler = exchangeHandlerFactory.create(request, context); } catch (final MisdirectedRequestException ex) { handler = new ImmediateResponseExchangeHandler(HttpStatus.SC_MISDIRECTED_REQUEST, ex.getMessage()); } catch (final HttpException ex) { handler = new ImmediateResponseExchangeHandler(HttpStatus.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); } if (handler == null) { handler = new ImmediateResponseExchangeHandler(HttpStatus.SC_NOT_FOUND, "Cannot handle request"); } exchangeHandler = handler; final ProtocolVersion transportVersion = request.getVersion(); if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) { throw new UnsupportedHttpVersionException(transportVersion); } context.setProtocolVersion(transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); try { httpProcessor.process(request, requestEntityDetails, context); exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel, context); } catch (final HttpException ex) { if (!responseCommitted.get()) { final HttpResponse response = new BasicHttpResponse(ServerSupport.toStatusCode(ex)); response.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); final AsyncResponseProducer responseProducer = new BasicResponseProducer(response, ServerSupport.toErrorMessage(ex)); exchangeHandler = new ImmediateResponseExchangeHandler(responseProducer); exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel, context); } else { throw ex; } } } boolean isOutputReady() { switch (responseState) { case BODY: return exchangeHandler.available() > 0; default: return false; } } void produceOutput() throws HttpException, IOException { switch (responseState) { case BODY: exchangeHandler.produce(internalDataChannel); break; } } void consumeData(final ByteBuffer src) throws HttpException, IOException { if (done.get() || requestState != MessageState.BODY) { throw new ProtocolException("Unexpected message data"); } if (responseState == MessageState.ACK) { outputChannel.requestOutput(); } exchangeHandler.consume(src); } void updateCapacity(final CapacityChannel capacityChannel) throws IOException { exchangeHandler.updateCapacity(capacityChannel); } void dataEnd(final List trailers) throws HttpException, IOException { if (done.get() || requestState != MessageState.BODY) { throw new ProtocolException("Unexpected message data"); } requestState = MessageState.COMPLETE; exchangeHandler.streamEnd(trailers); } void failed(final Exception cause) { if (!done.get()) { exchangeHandler.failed(cause); } } @Override public void releaseResources() { if (done.compareAndSet(false, true)) { requestState = MessageState.COMPLETE; responseState = MessageState.COMPLETE; exchangeHandler.releaseResources(); } } void appendState(final StringBuilder buf) { buf.append("requestState=").append(requestState) .append(", responseState=").append(responseState) .append(", responseCommitted=").append(responseCommitted) .append(", keepAlive=").append(keepAlive) .append(", done=").append(done); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append("["); appendState(buf); buf.append("]"); return buf.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexerFactory.java0100664 0000000 0000000 00000015164 14245617503 031571 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.NHttpMessageParserFactory; import org.apache.hc.core5.http.nio.NHttpMessageWriterFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; /** * {@link ServerHttp1StreamDuplexer} factory. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) @Internal public final class ServerHttp1StreamDuplexerFactory { private final HttpProcessor httpProcessor; private final HandlerFactory exchangeHandlerFactory; private final ConnectionReuseStrategy connectionReuseStrategy; private final Http1Config http1Config; private final CharCodingConfig charCodingConfig; private final NHttpMessageParserFactory requestParserFactory; private final NHttpMessageWriterFactory responseWriterFactory; private final ContentLengthStrategy incomingContentStrategy; private final ContentLengthStrategy outgoingContentStrategy; private final Http1StreamListener streamListener; public ServerHttp1StreamDuplexerFactory( final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ConnectionReuseStrategy connectionReuseStrategy, final NHttpMessageParserFactory requestParserFactory, final NHttpMessageWriterFactory responseWriterFactory, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final Http1StreamListener streamListener) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.exchangeHandlerFactory = Args.notNull(exchangeHandlerFactory, "Exchange handler factory"); this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.connectionReuseStrategy = connectionReuseStrategy != null ? connectionReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE; this.requestParserFactory = requestParserFactory != null ? requestParserFactory : new DefaultHttpRequestParserFactory(http1Config); this.responseWriterFactory = responseWriterFactory != null ? responseWriterFactory : DefaultHttpResponseWriterFactory.INSTANCE; this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy : DefaultContentLengthStrategy.INSTANCE; this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy : DefaultContentLengthStrategy.INSTANCE; this.streamListener = streamListener; } public ServerHttp1StreamDuplexerFactory( final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ConnectionReuseStrategy connectionReuseStrategy, final NHttpMessageParserFactory requestParserFactory, final NHttpMessageWriterFactory responseWriterFactory, final Http1StreamListener streamListener) { this(httpProcessor, exchangeHandlerFactory, http1Config, charCodingConfig, connectionReuseStrategy, requestParserFactory, responseWriterFactory, null, null, streamListener); } public ServerHttp1StreamDuplexerFactory( final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final Http1StreamListener streamListener) { this(httpProcessor, exchangeHandlerFactory, http1Config, charCodingConfig, null, null ,null, streamListener); } public ServerHttp1StreamDuplexer create(final String scheme, final ProtocolIOSession ioSession) { return new ServerHttp1StreamDuplexer(ioSession, httpProcessor, exchangeHandlerFactory, scheme, http1Config, charCodingConfig, connectionReuseStrategy, requestParserFactory.create(), responseWriterFactory.create(), incomingContentStrategy, outgoingContentStrategy, streamListener); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpResponseParser.java0100664 0000000 0000000 00000006500 14245617503 030277 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.LineParser; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Default {@link org.apache.hc.core5.http.nio.NHttpMessageParser} implementation for {@link HttpResponse}s. * * @since 4.1 */ public class DefaultHttpResponseParser extends AbstractMessageParser { private final HttpResponseFactory responseFactory; /** * Creates an instance of DefaultHttpResponseParser. * * @param responseFactory the response factory. * @param parser the line parser. If {@code null} * {@link org.apache.hc.core5.http.message.LazyLineParser#INSTANCE} will be used. * @param http1Config Message http1Config. If {@code null} * {@link Http1Config#DEFAULT} will be used. * * @since 4.3 */ public DefaultHttpResponseParser( final HttpResponseFactory responseFactory, final LineParser parser, final Http1Config http1Config) { super(parser, http1Config); this.responseFactory = Args.notNull(responseFactory, "Response factory"); } /** * @since 4.3 */ public DefaultHttpResponseParser(final HttpResponseFactory responseFactory, final Http1Config http1Config) { this(responseFactory, null, http1Config); } /** * @since 4.3 */ public DefaultHttpResponseParser(final HttpResponseFactory responseFactory) { this(responseFactory, null); } @Override protected T createMessage(final CharArrayBuffer buffer) throws HttpException { final StatusLine statusLine = getLineParser().parseStatusLine(buffer); final T response = this.responseFactory.newHttpResponse(statusLine.getStatusCode(), statusLine.getReasonPhrase()); response.setVersion(statusLine.getProtocolVersion()); return response; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentEncoder.java0100664 0000000 0000000 00000014241 14245617503 027556 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.ContentEncoder; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; /** * Abstract {@link ContentEncoder} that serves as a base for all content * encoder implementations. * * @since 4.0 */ public abstract class AbstractContentEncoder implements ContentEncoder { final WritableByteChannel channel; final SessionOutputBuffer buffer; final BasicHttpTransportMetrics metrics; boolean completed; /** * Creates an instance of this class. * * @param channel the destination channel. * @param buffer the session output buffer that can be used to store * session data for intermediate processing. * @param metrics Transport metrics of the underlying HTTP transport. */ public AbstractContentEncoder( final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics) { super(); Args.notNull(channel, "Channel"); Args.notNull(buffer, "Session input buffer"); Args.notNull(metrics, "Transport metrics"); this.buffer = buffer; this.channel = channel; this.metrics = metrics; } protected WritableByteChannel channel() { return this.channel; } protected SessionOutputBuffer buffer() { return this.buffer; } protected BasicHttpTransportMetrics metrics() { return this.metrics; } @Override public boolean isCompleted() { return this.completed; } @Override public void complete(final List trailers) throws IOException { this.completed = true; } public final void complete() throws IOException { complete(null); } protected void assertNotCompleted() { Asserts.check(!this.completed, "Encoding process already completed"); } /** * Flushes content of the session buffer to the channel and updates transport metrics. * * @return number of bytes written to the channel. * * @since 4.3 */ protected int flushToChannel() throws IOException { if (!this.buffer.hasData()) { return 0; } final int bytesWritten = this.buffer.flush(this.channel); if (bytesWritten > 0) { this.metrics.incrementBytesTransferred(bytesWritten); } return bytesWritten; } /** * Flushes content of the given buffer to the channel and updates transport metrics. * * @return number of bytes written to the channel. * * @since 4.3 */ protected int writeToChannel(final ByteBuffer src) throws IOException { if (!src.hasRemaining()) { return 0; } final int bytesWritten = this.channel.write(src); if (bytesWritten > 0) { this.metrics.incrementBytesTransferred(bytesWritten); } return bytesWritten; } /** * Transfers content of the source to the channel and updates transport metrics. * * @param src source. * @param limit max number of bytes to transfer. * @return number of bytes transferred. * * @since 4.3 */ protected int writeToChannel(final ByteBuffer src, final int limit) throws IOException { return doWriteChunk(src, limit, true); } /** * Transfers content of the source to the buffer and updates transport metrics. * * @param src source. * @param limit max number of bytes to transfer. * @return number of bytes transferred. * * @since 4.3 */ protected int writeToBuffer(final ByteBuffer src, final int limit) throws IOException { return doWriteChunk(src, limit, false); } private int doWriteChunk( final ByteBuffer src, final int chunk, final boolean direct) throws IOException { final int bytesWritten; if (src.remaining() > chunk) { final int oldLimit = src.limit(); final int newLimit = oldLimit - (src.remaining() - chunk); src.limit(newLimit); bytesWritten = doWriteChunk(src, direct); src.limit(oldLimit); } else { bytesWritten = doWriteChunk(src, direct); } return bytesWritten; } private int doWriteChunk(final ByteBuffer src, final boolean direct) throws IOException { if (direct) { final int bytesWritten = this.channel.write(src); if (bytesWritten > 0) { this.metrics.incrementBytesTransferred(bytesWritten); } return bytesWritten; } final int chunk = src.remaining(); this.buffer.write(src); return chunk; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/HttpConnectionEventHandler.java0100664 0000000 0000000 00000003051 14245617503 030414 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.reactor.IOEventHandler; /** * {@link IOEventHandler} that also exposes {@link HttpConnection} properties. * * @since 5.0 */ @Internal public interface HttpConnectionEventHandler extends IOEventHandler, HttpConnection { } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/Http1StreamChannel.java0100664 0000000 0000000 00000003625 14245617503 026631 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.nio.ContentEncoder; import org.apache.hc.core5.util.Timeout; interface Http1StreamChannel extends ContentEncoder { void close(); void activate() throws HttpException, IOException; void submit(OutgoingMessage messageHead, boolean endStream, final FlushMode flushMode) throws HttpException, IOException; void requestOutput(); void suspendOutput() throws IOException; boolean abortGracefully() throws IOException; Timeout getSocketTimeout(); void setSocketTimeout(Timeout timeout); } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkDecoder.java0100664 0000000 0000000 00000024720 14245617503 025521 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.MalformedChunkCodingException; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.TruncatedChunkException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.message.BufferedHeader; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Implements chunked transfer decoding. The content is received in small chunks. * Entities transferred using this encoder can be of unlimited length. * * @since 4.0 */ public class ChunkDecoder extends AbstractContentDecoder { private enum State { READ_CONTENT, READ_FOOTERS, COMPLETED } private State state; private boolean endOfChunk; private boolean endOfStream; private CharArrayBuffer lineBuf; private long chunkSize; private long pos; private final Http1Config http1Config; private final List trailerBufs; private final List

trailers; /** * @since 4.4 */ public ChunkDecoder( final ReadableByteChannel channel, final SessionInputBuffer buffer, final Http1Config http1Config, final BasicHttpTransportMetrics metrics) { super(channel, buffer, metrics); this.state = State.READ_CONTENT; this.chunkSize = -1L; this.pos = 0L; this.endOfChunk = false; this.endOfStream = false; this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.trailerBufs = new ArrayList<>(); this.trailers = new ArrayList<>(); } public ChunkDecoder( final ReadableByteChannel channel, final SessionInputBuffer buffer, final BasicHttpTransportMetrics metrics) { this(channel, buffer, null, metrics); } private void readChunkHead() throws IOException { if (this.lineBuf == null) { this.lineBuf = new CharArrayBuffer(32); } else { this.lineBuf.clear(); } if (this.endOfChunk) { if (this.buffer.readLine(this.lineBuf, this.endOfStream)) { if (!this.lineBuf.isEmpty()) { throw new MalformedChunkCodingException("CRLF expected at end of chunk"); } } else { if (this.buffer.length() > 2 || this.endOfStream) { throw new MalformedChunkCodingException("CRLF expected at end of chunk"); } return; } this.endOfChunk = false; } final boolean lineComplete = this.buffer.readLine(this.lineBuf, this.endOfStream); final int maxLineLen = this.http1Config.getMaxLineLength(); if (maxLineLen > 0 && (this.lineBuf.length() > maxLineLen || (!lineComplete && this.buffer.length() > maxLineLen))) { throw new MessageConstraintException("Maximum line length limit exceeded"); } if (lineComplete) { int separator = this.lineBuf.indexOf(';'); if (separator < 0) { separator = this.lineBuf.length(); } final String s = this.lineBuf.substringTrimmed(0, separator); try { this.chunkSize = Long.parseLong(s, 16); } catch (final NumberFormatException e) { throw new MalformedChunkCodingException("Bad chunk header: " + s); } this.pos = 0L; } else if (this.endOfStream) { throw new ConnectionClosedException( "Premature end of chunk coded message body: closing chunk expected"); } } private void parseHeader() throws IOException { final CharArrayBuffer current = this.lineBuf; final int count = this.trailerBufs.size(); if ((this.lineBuf.charAt(0) == ' ' || this.lineBuf.charAt(0) == '\t') && count > 0) { // Handle folded header line final CharArrayBuffer previous = this.trailerBufs.get(count - 1); int i = 0; while (i < current.length()) { final char ch = current.charAt(i); if (ch != ' ' && ch != '\t') { break; } i++; } final int maxLineLen = this.http1Config.getMaxLineLength(); if (maxLineLen > 0 && previous.length() + 1 + current.length() - i > maxLineLen) { throw new MessageConstraintException("Maximum line length limit exceeded"); } previous.append(' '); previous.append(current, i, current.length() - i); } else { this.trailerBufs.add(current); this.lineBuf = null; } } private void processFooters() throws IOException { final int count = this.trailerBufs.size(); if (count > 0) { this.trailers.clear(); for (int i = 0; i < this.trailerBufs.size(); i++) { try { this.trailers.add(new BufferedHeader(this.trailerBufs.get(i))); } catch (final ParseException ex) { throw new IOException(ex); } } } this.trailerBufs.clear(); } @Override public int read(final ByteBuffer dst) throws IOException { Args.notNull(dst, "Byte buffer"); if (this.state == State.COMPLETED) { return -1; } int totalRead = 0; while (this.state != State.COMPLETED) { if (!this.buffer.hasData() || this.chunkSize == -1L) { final int bytesRead = fillBufferFromChannel(); if (bytesRead == -1) { this.endOfStream = true; } } switch (this.state) { case READ_CONTENT: if (this.chunkSize == -1L) { readChunkHead(); if (this.chunkSize == -1L) { // Unable to read a chunk head return totalRead; } if (this.chunkSize == 0L) { // Last chunk. Read footers this.chunkSize = -1L; this.state = State.READ_FOOTERS; break; } } final long maxLen = this.chunkSize - this.pos; final int len = this.buffer.read(dst, (int) Math.min(maxLen, Integer.MAX_VALUE)); if (len > 0) { this.pos += len; totalRead += len; } else { if (!this.buffer.hasData() && this.endOfStream) { this.state = State.COMPLETED; setCompleted(); throw new TruncatedChunkException( "Truncated chunk (expected size: %d; actual size: %d)", chunkSize, pos); } } if (this.pos == this.chunkSize) { // At the end of the chunk this.chunkSize = -1L; this.pos = 0L; this.endOfChunk = true; break; } return totalRead; case READ_FOOTERS: if (this.lineBuf == null) { this.lineBuf = new CharArrayBuffer(32); } else { this.lineBuf.clear(); } if (!this.buffer.readLine(this.lineBuf, this.endOfStream)) { // Unable to read a footer if (this.endOfStream) { this.state = State.COMPLETED; setCompleted(); } return totalRead; } if (this.lineBuf.length() > 0) { final int maxHeaderCount = this.http1Config.getMaxHeaderCount(); if (maxHeaderCount > 0 && trailerBufs.size() >= maxHeaderCount) { throw new MessageConstraintException("Maximum header count exceeded"); } parseHeader(); } else { this.state = State.COMPLETED; setCompleted(); processFooters(); } break; } } return totalRead; } @Override public List getTrailers() { return this.trailers.isEmpty() ? null : new ArrayList<>(this.trailers); } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("[chunk-coded; completed: "); sb.append(this.completed); sb.append("]"); return sb.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpRequestWriterFactory.java0100664 0000000 0000000 00000004567 14245617503 031514 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.message.BasicLineFormatter; import org.apache.hc.core5.http.message.LineFormatter; import org.apache.hc.core5.http.nio.NHttpMessageWriter; import org.apache.hc.core5.http.nio.NHttpMessageWriterFactory; /** * Default factory for request message writers. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultHttpRequestWriterFactory implements NHttpMessageWriterFactory { public static final DefaultHttpRequestWriterFactory INSTANCE = new DefaultHttpRequestWriterFactory(); private final LineFormatter lineFormatter; public DefaultHttpRequestWriterFactory(final LineFormatter lineFormatter) { super(); this.lineFormatter = lineFormatter != null ? lineFormatter : BasicLineFormatter.INSTANCE; } public DefaultHttpRequestWriterFactory() { this(null); } @Override public NHttpMessageWriter create() { return new DefaultHttpRequestWriter<>(this.lineFormatter); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ExpandableBuffer.java0100664 0000000 0000000 00000015363 14403631147 026357 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import org.apache.hc.core5.annotation.Internal; /** * A buffer that expand its capacity on demand. Internally, this class is backed * by an instance of {@link ByteBuffer}. *

* This class is not thread safe. *

* @since 4.0 */ @Internal public class ExpandableBuffer { public enum Mode { INPUT, OUTPUT } private Mode mode; private ByteBuffer buffer; /** * Allocates buffer of the given size using the given allocator. *

* Sets the mode to input. *

* * @param bufferSize the buffer size. */ protected ExpandableBuffer(final int bufferSize) { super(); this.buffer = ByteBuffer.allocate(bufferSize); this.mode = Mode.INPUT; } /** * Returns the current mode: *

* {@link Mode#INPUT}: the buffer is in the input mode. *

* {@link Mode#OUTPUT}: the buffer is in the output mode. * * @return current input/output mode. */ protected Mode mode() { return this.mode; } protected ByteBuffer buffer() { return this.buffer; } /** * Sets the mode to output. The buffer can now be read from. */ protected void setOutputMode() { if (this.mode != Mode.OUTPUT) { this.buffer.flip(); this.mode = Mode.OUTPUT; } } /** * Sets the mode to input. The buffer can now be written into. */ protected void setInputMode() { if (this.mode != Mode.INPUT) { if (this.buffer.hasRemaining()) { this.buffer.compact(); } else { this.buffer.clear(); } this.mode = Mode.INPUT; } } private void expandCapacity(final int capacity) { final ByteBuffer oldBuffer = this.buffer; this.buffer = ByteBuffer.allocate(capacity); oldBuffer.flip(); this.buffer.put(oldBuffer); } /** * Expands buffer's capacity. * * @throws BufferOverflowException in case we get over the maximum allowed value */ protected void expand() throws BufferOverflowException { int newcapacity = (this.buffer.capacity() + 1) << 1; if (newcapacity < 0) { final int vmBytes = Long.SIZE >> 3; final int javaBytes = 8; // this is to be checked when the JVM version changes @SuppressWarnings("unused") // we really need the 8 if we're going to make this foolproof final int headRoom = Math.max(vmBytes, javaBytes); // Reason: In GC the size of objects is passed as int (2 bytes). // Then, the header size of the objects is added to the size. // Long has the longest header available. Object header seems to be linked to it. // Details: I added a minimum of 8 just to be safe and because 8 is used in // java.lang.Object.ArrayList: private static final int MAX_ARRAY_SIZE = 2147483639. // // WARNING: This code assumes you are providing enough heap room with -Xmx. // source of inspiration: https://bugs.openjdk.java.net/browse/JDK-8059914 newcapacity = Integer.MAX_VALUE - headRoom; if (newcapacity <= this.buffer.capacity()) { throw new BufferOverflowException(); } } expandCapacity(newcapacity); } /** * Ensures the buffer can accommodate the exact required capacity. * * @param requiredCapacity the required capacity. */ protected void ensureCapacity(final int requiredCapacity) { if (requiredCapacity > this.buffer.capacity()) { expandCapacity(requiredCapacity); } } /** * Ensures the buffer can accommodate at least the required capacity adjusted to multiples of 1024. * * @param requiredCapacity the required capacity. */ protected void ensureAdjustedCapacity(final int requiredCapacity) { if (requiredCapacity > this.buffer.capacity()) { final int adjustedCapacity = ((requiredCapacity >> 10) + 1) << 10; expandCapacity(adjustedCapacity); } } /** * Determines if the buffer contains data. *

* Sets the mode to output. *

* * @return {@code true} if there is data in the buffer, * {@code false} otherwise. */ protected boolean hasData() { setOutputMode(); return this.buffer.hasRemaining(); } /** * Returns the length of this buffer. *

* Sets the mode to output. *

* * @return buffer length. */ protected int length() { setOutputMode(); return this.buffer.remaining(); } /** * Returns available capacity of this buffer. * * @return buffer length. */ protected int capacity() { setInputMode(); return this.buffer.remaining(); } /** * Clears buffer. *

* Sets the mode to input. *

*/ protected void clear() { this.buffer.clear(); this.mode = Mode.INPUT; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("[mode="); sb.append(this.mode); sb.append(" pos="); sb.append(this.buffer.position()); sb.append(" lim="); sb.append(this.buffer.limit()); sb.append(" cap="); sb.append(this.buffer.capacity()); sb.append("]"); return sb.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkEncoder.java0100664 0000000 0000000 00000013270 14403631147 025525 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.util.List; import org.apache.hc.core5.http.FormattedHeader; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.message.BasicLineFormatter; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.util.CharArrayBuffer; /** * Implements chunked transfer coding. The content is sent in small chunks. * Entities transferred using this decoder can be of unlimited length. * * @since 4.0 */ public class ChunkEncoder extends AbstractContentEncoder { private final int chunkSizeHint; private final CharArrayBuffer lineBuffer; /** * @param channel underlying channel. * @param buffer session buffer. * @param metrics transport metrics. * * @since 5.0 */ public ChunkEncoder( final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics, final int chunkSizeHint) { super(channel, buffer, metrics); this.chunkSizeHint = Math.max(chunkSizeHint, 0); this.lineBuffer = new CharArrayBuffer(16); } public ChunkEncoder( final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics) { this(channel, buffer, metrics, 0); } @Override public int write(final ByteBuffer src) throws IOException { if (src == null) { return 0; } assertNotCompleted(); int total = 0; while (src.hasRemaining()) { int chunk = src.remaining(); int avail; avail = this.buffer.capacity(); // subtract the length of the longest chunk header // 12345678\r\n // \r\n avail -= 12; if (avail > 0) { if (avail < chunk) { // write no more than 'avail' bytes chunk = avail; this.lineBuffer.clear(); this.lineBuffer.append(Integer.toHexString(chunk)); this.buffer.writeLine(this.lineBuffer); final int oldlimit = src.limit(); src.limit(src.position() + chunk); this.buffer.write(src); src.limit(oldlimit); } else { // write all this.lineBuffer.clear(); this.lineBuffer.append(Integer.toHexString(chunk)); this.buffer.writeLine(this.lineBuffer); this.buffer.write(src); } this.lineBuffer.clear(); this.buffer.writeLine(this.lineBuffer); total += chunk; } if (this.buffer.length() >= this.chunkSizeHint || src.hasRemaining()) { final int bytesWritten = flushToChannel(); if (bytesWritten == 0) { break; } } } return total; } @Override public void complete(final List trailers) throws IOException { assertNotCompleted(); this.lineBuffer.clear(); this.lineBuffer.append("0"); this.buffer.writeLine(this.lineBuffer); writeTrailers(trailers); this.lineBuffer.clear(); this.buffer.writeLine(this.lineBuffer); super.complete(trailers); } private void writeTrailers(final List trailers) throws IOException { if (trailers != null) { for (int i = 0; i < trailers.size(); i++) { final Header header = trailers.get(i); if (header instanceof FormattedHeader) { final CharArrayBuffer chbuffer = ((FormattedHeader) header).getBuffer(); buffer.writeLine(chbuffer); } else { this.lineBuffer.clear(); BasicLineFormatter.INSTANCE.formatHeader(this.lineBuffer, header); buffer.writeLine(this.lineBuffer); } } } } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("[chunk-coded; completed: "); sb.append(isCompleted()); sb.append("]"); return sb.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractMessageWriter.java0100664 0000000 0000000 00000007403 14245617503 027427 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.util.Iterator; import org.apache.hc.core5.http.FormattedHeader; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.message.BasicLineFormatter; import org.apache.hc.core5.http.message.LineFormatter; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.http.nio.NHttpMessageWriter; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Abstract {@link NHttpMessageWriter} that serves as a base for all message * writer implementations. * * @since 4.0 */ public abstract class AbstractMessageWriter implements NHttpMessageWriter { private final CharArrayBuffer lineBuf; private final LineFormatter lineFormatter; /** * Creates an instance of AbstractMessageWriter. * * @param formatter the line formatter If {@code null} {@link BasicLineFormatter#INSTANCE} * will be used. * * @since 4.3 */ public AbstractMessageWriter(final LineFormatter formatter) { super(); this.lineFormatter = (formatter != null) ? formatter : BasicLineFormatter.INSTANCE; this.lineBuf = new CharArrayBuffer(64); } LineFormatter getLineFormatter() { return this.lineFormatter; } @Override public void reset() { } /** * Writes out the first line of {@link HttpMessage}. * * @param message HTTP message. */ protected abstract void writeHeadLine(T message, CharArrayBuffer buffer) throws IOException; @Override public void write(final T message, final SessionOutputBuffer sessionBuffer) throws IOException, HttpException { Args.notNull(message, "HTTP message"); Args.notNull(sessionBuffer, "Session output buffer"); writeHeadLine(message, this.lineBuf); sessionBuffer.writeLine(this.lineBuf); for (final Iterator
it = message.headerIterator(); it.hasNext(); ) { final Header header = it.next(); if (header instanceof FormattedHeader) { final CharArrayBuffer buffer = ((FormattedHeader) header).getBuffer(); sessionBuffer.writeLine(buffer); } else { this.lineBuf.clear(); this.lineFormatter.formatHeader(this.lineBuf, header); sessionBuffer.writeLine(this.lineBuf); } } this.lineBuf.clear(); sessionBuffer.writeLine(this.lineBuf); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpResponseWriter.java0100664 0000000 0000000 00000005220 14245617503 030315 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.LineFormatter; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.util.CharArrayBuffer; /** * Default {@link org.apache.hc.core5.http.nio.NHttpMessageWriter} implementation for {@link HttpResponse}s. * * @since 4.1 */ public class DefaultHttpResponseWriter extends AbstractMessageWriter { /** * Creates an instance of DefaultHttpResponseWriter. * * @param formatter the line formatter If {@code null} * {@link org.apache.hc.core5.http.message.BasicLineFormatter#INSTANCE} will be used. * * @since 4.3 */ public DefaultHttpResponseWriter(final LineFormatter formatter) { super(formatter); } /** * @since 4.3 */ public DefaultHttpResponseWriter() { super(null); } @Override protected void writeHeadLine(final T message, final CharArrayBuffer lineBuf) throws IOException { lineBuf.clear(); final ProtocolVersion transportVersion = message.getVersion(); getLineFormatter().formatStatusLine(lineBuf, new StatusLine( transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1, message.getCode(), message.getReasonPhrase())); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpResponseParserFactory.java0100664 0000000 0000000 00000006022 14245617503 031626 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.LazyLaxLineParser; import org.apache.hc.core5.http.message.LineParser; import org.apache.hc.core5.http.nio.NHttpMessageParser; import org.apache.hc.core5.http.nio.NHttpMessageParserFactory; /** * Default factory for response message parsers. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultHttpResponseParserFactory implements NHttpMessageParserFactory { public static final DefaultHttpResponseParserFactory INSTANCE = new DefaultHttpResponseParserFactory(); private final Http1Config http1Config; private final HttpResponseFactory responseFactory; private final LineParser lineParser; public DefaultHttpResponseParserFactory( final Http1Config http1Config, final HttpResponseFactory responseFactory, final LineParser lineParser) { super(); this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.responseFactory = responseFactory != null ? responseFactory : DefaultHttpResponseFactory.INSTANCE; this.lineParser = lineParser != null ? lineParser : LazyLaxLineParser.INSTANCE; } public DefaultHttpResponseParserFactory(final Http1Config http1Config) { this(http1Config, null, null); } public DefaultHttpResponseParserFactory() { this(null); } @Override public NHttpMessageParser create() { return new DefaultHttpResponseParser<>(responseFactory, lineParser, http1Config); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractMessageParser.java0100664 0000000 0000000 00000016473 14245617503 027416 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.LazyLineParser; import org.apache.hc.core5.http.message.LineParser; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.apache.hc.core5.http.nio.NHttpMessageParser; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Abstract {@link NHttpMessageParser} that serves as a base for all message * parser implementations. * * @since 4.0 */ public abstract class AbstractMessageParser implements NHttpMessageParser { private enum State { READ_HEAD_LINE, READ_HEADERS, COMPLETED } private State state; private T message; private CharArrayBuffer lineBuf; private final List headerBufs; private int emptyLineCount; private final LineParser lineParser; private final Http1Config messageConstraints; /** * Creates an instance of AbstractMessageParser. * * @param lineParser the line parser. If {@code null} * {@link org.apache.hc.core5.http.message.LazyLineParser#INSTANCE} will be used. * @param messageConstraints Message constraints. If {@code null} * {@link Http1Config#DEFAULT} will be used. * * @since 4.3 */ public AbstractMessageParser(final LineParser lineParser, final Http1Config messageConstraints) { super(); this.lineParser = lineParser != null ? lineParser : LazyLineParser.INSTANCE; this.messageConstraints = messageConstraints != null ? messageConstraints : Http1Config.DEFAULT; this.headerBufs = new ArrayList<>(); this.state = State.READ_HEAD_LINE; } LineParser getLineParser() { return this.lineParser; } @Override public void reset() { this.state = State.READ_HEAD_LINE; this.headerBufs.clear(); this.emptyLineCount = 0; this.message = null; } /** * Creates {@link HttpMessage} instance based on the content of the input * buffer containing the first line of the incoming HTTP message. * * @param buffer the line buffer. * @return HTTP message. * @throws HttpException in case of HTTP protocol violation */ protected abstract T createMessage(CharArrayBuffer buffer) throws HttpException; private T parseHeadLine() throws IOException, HttpException { if (this.lineBuf.isEmpty()) { this.emptyLineCount++; if (this.emptyLineCount >= this.messageConstraints.getMaxEmptyLineCount()) { throw new MessageConstraintException("Maximum empty line limit exceeded"); } return null; } return createMessage(this.lineBuf); } private void parseHeader() throws IOException { final CharArrayBuffer current = this.lineBuf; final int count = this.headerBufs.size(); if ((this.lineBuf.charAt(0) == ' ' || this.lineBuf.charAt(0) == '\t') && count > 0) { // Handle folded header line final CharArrayBuffer previous = this.headerBufs.get(count - 1); int i = 0; while (i < current.length()) { final char ch = current.charAt(i); if (ch != ' ' && ch != '\t') { break; } i++; } final int maxLineLen = this.messageConstraints.getMaxLineLength(); if (maxLineLen > 0 && previous.length() + 1 + current.length() - i > maxLineLen) { throw new MessageConstraintException("Maximum line length limit exceeded"); } previous.append(' '); previous.append(current, i, current.length() - i); } else { this.headerBufs.add(current); this.lineBuf = null; } } @Override public T parse( final SessionInputBuffer sessionBuffer, final boolean endOfStream) throws IOException, HttpException { Args.notNull(sessionBuffer, "Session input buffer"); while (this.state !=State.COMPLETED) { if (this.lineBuf == null) { this.lineBuf = new CharArrayBuffer(64); } else { this.lineBuf.clear(); } final boolean lineComplete = sessionBuffer.readLine(this.lineBuf, endOfStream); final int maxLineLen = this.messageConstraints.getMaxLineLength(); if (maxLineLen > 0 && (this.lineBuf.length() > maxLineLen || (!lineComplete && sessionBuffer.length() > maxLineLen))) { throw new MessageConstraintException("Maximum line length limit exceeded"); } if (!lineComplete) { break; } switch (this.state) { case READ_HEAD_LINE: this.message = parseHeadLine(); if (this.message != null) { this.state = State.READ_HEADERS; } break; case READ_HEADERS: if (this.lineBuf.length() > 0) { final int maxHeaderCount = this.messageConstraints.getMaxHeaderCount(); if (maxHeaderCount > 0 && headerBufs.size() >= maxHeaderCount) { throw new MessageConstraintException("Maximum header count exceeded"); } parseHeader(); } else { this.state = State.COMPLETED; } break; } if (endOfStream && !sessionBuffer.hasData()) { this.state = State.COMPLETED; } } if (this.state ==State. COMPLETED) { for (final CharArrayBuffer buffer : this.headerBufs) { this.message.addHeader(this.lineParser.parseHeader(buffer)); } return this.message; } return null; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/MessageState.java0100664 0000000 0000000 00000002414 14245617503 025544 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; public enum MessageState { IDLE, HEADERS, ACK, BODY, COMPLETE } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpRequestParser.java0100664 0000000 0000000 00000007534 14245617503 030141 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestFactory; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.RequestHeaderFieldsTooLargeException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.LineParser; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Default {@link org.apache.hc.core5.http.nio.NHttpMessageParser} implementation for {@link HttpRequest}s. * * @since 4.1 */ public class DefaultHttpRequestParser extends AbstractMessageParser { private final HttpRequestFactory requestFactory; /** * Creates an instance of DefaultHttpRequestParser. * * @param requestFactory the request factory. * @param parser the line parser. If {@code null} * {@link org.apache.hc.core5.http.message.LazyLineParser#INSTANCE} will be used. * @param http1Config Message http1Config. If {@code null} * {@link Http1Config#DEFAULT} will be used. * * @since 4.3 */ public DefaultHttpRequestParser( final HttpRequestFactory requestFactory, final LineParser parser, final Http1Config http1Config) { super(parser, http1Config); this.requestFactory = Args.notNull(requestFactory, "Request factory"); } /** * @since 4.3 */ public DefaultHttpRequestParser(final HttpRequestFactory requestFactory, final Http1Config http1Config) { this(requestFactory, null, http1Config); } /** * @since 4.3 */ public DefaultHttpRequestParser(final HttpRequestFactory requestFactory) { this(requestFactory, null); } @Override public T parse(final SessionInputBuffer sessionBuffer, final boolean endOfStream) throws IOException, HttpException { try { return super.parse(sessionBuffer, endOfStream); } catch (final MessageConstraintException ex) { throw new RequestHeaderFieldsTooLargeException(ex.getMessage(), ex); } } @Override protected T createMessage(final CharArrayBuffer buffer) throws HttpException { final RequestLine requestLine = getLineParser().parseRequestLine(buffer); final T request = this.requestFactory.newHttpRequest(requestLine.getMethod(), requestLine.getUri()); request.setVersion(requestLine.getProtocolVersion()); return request; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java0100664 0000000 0000000 00000062350 14403631147 030531 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SelectionKey; import java.nio.channels.WritableByteChannel; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import javax.net.ssl.SSLSession; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.BasicEndpointDetails; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.impl.CharCodingSupport; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.impl.IncomingEntityDetails; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.ContentDecoder; import org.apache.hc.core5.http.nio.ContentEncoder; import org.apache.hc.core5.http.nio.NHttpMessageParser; import org.apache.hc.core5.http.nio.NHttpMessageWriter; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.http.nio.command.CommandSupport; import org.apache.hc.core5.http.nio.command.RequestExecutionCommand; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.SocketTimeoutExceptionFactory; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.EventMask; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Identifiable; import org.apache.hc.core5.util.Timeout; abstract class AbstractHttp1StreamDuplexer implements Identifiable, HttpConnection { private enum ConnectionState { READY, ACTIVE, GRACEFUL_SHUTDOWN, SHUTDOWN} private final ProtocolIOSession ioSession; private final Http1Config http1Config; private final SessionInputBufferImpl inbuf; private final SessionOutputBufferImpl outbuf; private final BasicHttpTransportMetrics inTransportMetrics; private final BasicHttpTransportMetrics outTransportMetrics; private final BasicHttpConnectionMetrics connMetrics; private final NHttpMessageParser incomingMessageParser; private final NHttpMessageWriter outgoingMessageWriter; private final ContentLengthStrategy incomingContentStrategy; private final ContentLengthStrategy outgoingContentStrategy; private final ByteBuffer contentBuffer; private final AtomicInteger outputRequests; private volatile Message incomingMessage; private volatile Message outgoingMessage; private volatile ConnectionState connState; private volatile CapacityWindow capacityWindow; private volatile ProtocolVersion version; private volatile EndpointDetails endpointDetails; AbstractHttp1StreamDuplexer( final ProtocolIOSession ioSession, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final NHttpMessageParser incomingMessageParser, final NHttpMessageWriter outgoingMessageWriter, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy) { this.ioSession = Args.notNull(ioSession, "I/O session"); this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; final int bufferSize = this.http1Config.getBufferSize(); this.inbuf = new SessionInputBufferImpl(bufferSize, Math.min(bufferSize, 512), this.http1Config.getMaxLineLength(), CharCodingSupport.createDecoder(charCodingConfig)); this.outbuf = new SessionOutputBufferImpl(bufferSize, Math.min(bufferSize, 512), CharCodingSupport.createEncoder(charCodingConfig)); this.inTransportMetrics = new BasicHttpTransportMetrics(); this.outTransportMetrics = new BasicHttpTransportMetrics(); this.connMetrics = new BasicHttpConnectionMetrics(inTransportMetrics, outTransportMetrics); this.incomingMessageParser = incomingMessageParser; this.outgoingMessageWriter = outgoingMessageWriter; this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy : DefaultContentLengthStrategy.INSTANCE; this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy : DefaultContentLengthStrategy.INSTANCE; this.contentBuffer = ByteBuffer.allocate(this.http1Config.getBufferSize()); this.outputRequests = new AtomicInteger(0); this.connState = ConnectionState.READY; } @Override public String getId() { return ioSession.getId(); } boolean isActive() { return connState == ConnectionState.ACTIVE; } boolean isShuttingDown() { return connState.compareTo(ConnectionState.GRACEFUL_SHUTDOWN) >= 0; } void shutdownSession(final CloseMode closeMode) { if (closeMode == CloseMode.GRACEFUL) { connState = ConnectionState.GRACEFUL_SHUTDOWN; ioSession.enqueue(ShutdownCommand.GRACEFUL, Command.Priority.NORMAL); } else { connState = ConnectionState.SHUTDOWN; ioSession.close(); } } void shutdownSession(final Exception cause) { connState = ConnectionState.SHUTDOWN; try { terminate(cause); } finally { final CloseMode closeMode; if (cause instanceof ConnectionClosedException) { closeMode = CloseMode.GRACEFUL; } else if (cause instanceof IOException) { closeMode = CloseMode.IMMEDIATE; } else { closeMode = CloseMode.GRACEFUL; } ioSession.close(closeMode); } } abstract void disconnected(); abstract void terminate(final Exception exception); abstract void updateInputMetrics(IncomingMessage incomingMessage, BasicHttpConnectionMetrics connMetrics); abstract void updateOutputMetrics(OutgoingMessage outgoingMessage, BasicHttpConnectionMetrics connMetrics); abstract void consumeHeader(IncomingMessage messageHead, EntityDetails entityDetails) throws HttpException, IOException; abstract boolean handleIncomingMessage(IncomingMessage incomingMessage) throws HttpException; abstract boolean handleOutgoingMessage(OutgoingMessage outgoingMessage) throws HttpException; abstract ContentDecoder createContentDecoder( long contentLength, ReadableByteChannel channel, SessionInputBuffer buffer, BasicHttpTransportMetrics metrics) throws HttpException; abstract ContentEncoder createContentEncoder( long contentLength, WritableByteChannel channel, SessionOutputBuffer buffer, BasicHttpTransportMetrics metrics) throws HttpException; abstract void consumeData(ByteBuffer src) throws HttpException, IOException; abstract void updateCapacity(CapacityChannel capacityChannel) throws HttpException, IOException; abstract void dataEnd(List trailers) throws HttpException, IOException; abstract boolean isOutputReady(); abstract void produceOutput() throws HttpException, IOException; abstract void execute(RequestExecutionCommand executionCommand) throws HttpException, IOException; abstract void inputEnd() throws HttpException, IOException; abstract void outputEnd() throws HttpException, IOException; abstract boolean inputIdle(); abstract boolean outputIdle(); abstract boolean handleTimeout(); private void processCommands() throws HttpException, IOException { for (;;) { final Command command = ioSession.poll(); if (command == null) { return; } if (command instanceof ShutdownCommand) { final ShutdownCommand shutdownCommand = (ShutdownCommand) command; requestShutdown(shutdownCommand.getType()); } else if (command instanceof RequestExecutionCommand) { if (connState.compareTo(ConnectionState.GRACEFUL_SHUTDOWN) >= 0) { command.cancel(); } else { execute((RequestExecutionCommand) command); return; } } else { throw new HttpException("Unexpected command: " + command.getClass()); } } } public final void onConnect() throws HttpException, IOException { if (connState == ConnectionState.READY) { connState = ConnectionState.ACTIVE; processCommands(); } } IncomingMessage parseMessageHead(final boolean endOfStream) throws IOException, HttpException { final IncomingMessage messageHead = incomingMessageParser.parse(inbuf, endOfStream); if (messageHead != null) { incomingMessageParser.reset(); } return messageHead; } public final void onInput(final ByteBuffer src) throws HttpException, IOException { if (src != null) { final int n = src.remaining(); inbuf.put(src); inTransportMetrics.incrementBytesTransferred(n); } if (connState.compareTo(ConnectionState.GRACEFUL_SHUTDOWN) >= 0 && inbuf.hasData() && inputIdle()) { ioSession.clearEvent(SelectionKey.OP_READ); return; } boolean endOfStream = false; if (incomingMessage == null) { final int bytesRead = inbuf.fill(ioSession); if (bytesRead > 0) { inTransportMetrics.incrementBytesTransferred(bytesRead); } endOfStream = bytesRead == -1; } do { if (incomingMessage == null) { final IncomingMessage messageHead = parseMessageHead(endOfStream); if (messageHead != null) { this.version = messageHead.getVersion(); updateInputMetrics(messageHead, connMetrics); final ContentDecoder contentDecoder; if (handleIncomingMessage(messageHead)) { final long len = incomingContentStrategy.determineLength(messageHead); contentDecoder = createContentDecoder(len, ioSession, inbuf, inTransportMetrics); consumeHeader(messageHead, contentDecoder != null ? new IncomingEntityDetails(messageHead, len) : null); } else { consumeHeader(messageHead, null); contentDecoder = null; } capacityWindow = new CapacityWindow(http1Config.getInitialWindowSize(), ioSession); if (contentDecoder != null) { incomingMessage = new Message<>(messageHead, contentDecoder); } else { inputEnd(); if (connState.compareTo(ConnectionState.ACTIVE) == 0) { ioSession.setEvent(SelectionKey.OP_READ); } } } else { break; } } if (incomingMessage != null) { final ContentDecoder contentDecoder = incomingMessage.getBody(); // At present the consumer can be forced to consume data // over its declared capacity in order to avoid having // unprocessed message body content stuck in the session // input buffer final int bytesRead = contentDecoder.read(contentBuffer); if (bytesRead > 0) { contentBuffer.flip(); consumeData(contentBuffer); contentBuffer.clear(); final int capacity = capacityWindow.removeCapacity(bytesRead); if (capacity <= 0) { if (!contentDecoder.isCompleted()) { updateCapacity(capacityWindow); } } } if (contentDecoder.isCompleted()) { dataEnd(contentDecoder.getTrailers()); capacityWindow.close(); incomingMessage = null; ioSession.setEvent(SelectionKey.OP_READ); inputEnd(); } else if (bytesRead == 0) { break; } } } while (inbuf.hasData()); if (endOfStream && !inbuf.hasData()) { if (outputIdle() && inputIdle()) { requestShutdown(CloseMode.GRACEFUL); } else { shutdownSession(new ConnectionClosedException("Connection closed by peer")); } } } public final void onOutput() throws IOException, HttpException { ioSession.getLock().lock(); try { if (outbuf.hasData()) { final int bytesWritten = outbuf.flush(ioSession); if (bytesWritten > 0) { outTransportMetrics.incrementBytesTransferred(bytesWritten); } } } finally { ioSession.getLock().unlock(); } if (connState.compareTo(ConnectionState.SHUTDOWN) < 0) { final int pendingOutputRequests = outputRequests.get(); produceOutput(); final boolean outputPending = isOutputReady(); final boolean outputEnd; ioSession.getLock().lock(); try { if (!outputPending && !outbuf.hasData() && outputRequests.compareAndSet(pendingOutputRequests, 0)) { ioSession.clearEvent(SelectionKey.OP_WRITE); } else { outputRequests.addAndGet(-pendingOutputRequests); } outputEnd = outgoingMessage == null && !outbuf.hasData(); } finally { ioSession.getLock().unlock(); } if (outputEnd) { outputEnd(); if (connState.compareTo(ConnectionState.ACTIVE) == 0) { processCommands(); } else if (connState.compareTo(ConnectionState.GRACEFUL_SHUTDOWN) >= 0 && inputIdle() && outputIdle()) { connState = ConnectionState.SHUTDOWN; } } } if (connState.compareTo(ConnectionState.SHUTDOWN) >= 0) { ioSession.close(); } } public final void onTimeout(final Timeout timeout) throws IOException, HttpException { if (!handleTimeout()) { onException(SocketTimeoutExceptionFactory.create(timeout)); } } public final void onException(final Exception ex) { shutdownSession(ex); CommandSupport.failCommands(ioSession, ex); } public final void onDisconnect() { disconnected(); CommandSupport.cancelCommands(ioSession); } void requestShutdown(final CloseMode closeMode) { switch (closeMode) { case GRACEFUL: if (connState == ConnectionState.ACTIVE) { connState = ConnectionState.GRACEFUL_SHUTDOWN; } break; case IMMEDIATE: connState = ConnectionState.SHUTDOWN; break; } ioSession.setEvent(SelectionKey.OP_WRITE); } void commitMessageHead( final OutgoingMessage messageHead, final boolean endStream, final FlushMode flushMode) throws HttpException, IOException { ioSession.getLock().lock(); try { outgoingMessageWriter.write(messageHead, outbuf); updateOutputMetrics(messageHead, connMetrics); if (!endStream) { final ContentEncoder contentEncoder; if (handleOutgoingMessage(messageHead)) { final long len = outgoingContentStrategy.determineLength(messageHead); contentEncoder = createContentEncoder(len, ioSession, outbuf, outTransportMetrics); } else { contentEncoder = null; } if (contentEncoder != null) { outgoingMessage = new Message<>(messageHead, contentEncoder); } } outgoingMessageWriter.reset(); if (flushMode == FlushMode.IMMEDIATE) { final int bytesWritten = outbuf.flush(ioSession); if (bytesWritten > 0) { outTransportMetrics.incrementBytesTransferred(bytesWritten); } } ioSession.setEvent(EventMask.WRITE); } finally { ioSession.getLock().unlock(); } } void requestSessionInput() { ioSession.setEvent(SelectionKey.OP_READ); } void requestSessionOutput() { outputRequests.incrementAndGet(); ioSession.setEvent(SelectionKey.OP_WRITE); } Timeout getSessionTimeout() { return ioSession.getSocketTimeout(); } void setSessionTimeout(final Timeout timeout) { ioSession.setSocketTimeout(timeout); } void suspendSessionInput() { ioSession.clearEvent(SelectionKey.OP_READ); } void suspendSessionOutput() throws IOException { ioSession.getLock().lock(); try { if (outbuf.hasData()) { final int bytesWritten = outbuf.flush(ioSession); if (bytesWritten > 0) { outTransportMetrics.incrementBytesTransferred(bytesWritten); } } else { ioSession.clearEvent(SelectionKey.OP_WRITE); } } finally { ioSession.getLock().unlock(); } } int streamOutput(final ByteBuffer src) throws IOException { ioSession.getLock().lock(); try { if (outgoingMessage == null) { throw new ClosedChannelException(); } final ContentEncoder contentEncoder = outgoingMessage.getBody(); final int bytesWritten = contentEncoder.write(src); if (bytesWritten > 0) { ioSession.setEvent(SelectionKey.OP_WRITE); } return bytesWritten; } finally { ioSession.getLock().unlock(); } } enum MessageDelineation { NONE, CHUNK_CODED, MESSAGE_HEAD} MessageDelineation endOutputStream(final List trailers) throws IOException { ioSession.getLock().lock(); try { if (outgoingMessage == null) { return MessageDelineation.NONE; } final ContentEncoder contentEncoder = outgoingMessage.getBody(); contentEncoder.complete(trailers); ioSession.setEvent(SelectionKey.OP_WRITE); outgoingMessage = null; return contentEncoder instanceof ChunkEncoder ? MessageDelineation.CHUNK_CODED : MessageDelineation.MESSAGE_HEAD; } finally { ioSession.getLock().unlock(); } } boolean isOutputCompleted() { ioSession.getLock().lock(); try { if (outgoingMessage == null) { return true; } final ContentEncoder contentEncoder = outgoingMessage.getBody(); return contentEncoder.isCompleted(); } finally { ioSession.getLock().unlock(); } } @Override public void close() throws IOException { ioSession.enqueue(ShutdownCommand.GRACEFUL, Command.Priority.NORMAL); } @Override public void close(final CloseMode closeMode) { ioSession.enqueue(new ShutdownCommand(closeMode), Command.Priority.IMMEDIATE); } @Override public boolean isOpen() { return connState == ConnectionState.ACTIVE; } @Override public Timeout getSocketTimeout() { return ioSession.getSocketTimeout(); } @Override public void setSocketTimeout(final Timeout timeout) { ioSession.setSocketTimeout(timeout); } @Override public EndpointDetails getEndpointDetails() { if (endpointDetails == null) { endpointDetails = new BasicEndpointDetails( ioSession.getRemoteAddress(), ioSession.getLocalAddress(), connMetrics, ioSession.getSocketTimeout()); } return endpointDetails; } @Override public ProtocolVersion getProtocolVersion() { return version; } @Override public SocketAddress getRemoteAddress() { return ioSession.getRemoteAddress(); } @Override public SocketAddress getLocalAddress() { return ioSession.getLocalAddress(); } @Override public SSLSession getSSLSession() { final TlsDetails tlsDetails = ioSession.getTlsDetails(); return tlsDetails != null ? tlsDetails.getSSLSession() : null; } void appendState(final StringBuilder buf) { buf.append("connState=").append(connState) .append(", inbuf=").append(inbuf) .append(", outbuf=").append(outbuf) .append(", inputWindow=").append(capacityWindow != null ? capacityWindow.getWindow() : 0); } static class CapacityWindow implements CapacityChannel { private final IOSession ioSession; private final Object lock; private int window; private boolean closed; CapacityWindow(final int window, final IOSession ioSession) { this.window = window; this.ioSession = ioSession; this.lock = new Object(); } @Override public void update(final int increment) throws IOException { synchronized (lock) { if (closed) { return; } if (increment > 0) { updateWindow(increment); ioSession.setEvent(SelectionKey.OP_READ); } } } /** * Internal method for removing capacity. We don't need to check * if this channel is closed in it. */ int removeCapacity(final int delta) { synchronized (lock) { updateWindow(-delta); if (window <= 0) { ioSession.clearEvent(SelectionKey.OP_READ); } return window; } } private void updateWindow(final int delta) { int newValue = window + delta; // Math.addExact if (((window ^ newValue) & (delta ^ newValue)) < 0) { newValue = delta < 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE; } window = newValue; } /** * Closes the capacity channel, preventing user code from accidentally requesting * read events outside of the context of the request the channel was created for */ void close() { synchronized (lock) { closed = true; } } // visible for testing int getWindow() { return window; } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/IdentityDecoder.java0100664 0000000 0000000 00000010422 14245617503 026234 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.FileContentDecoder; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.apache.hc.core5.util.Args; /** * Content decoder that reads data without any transformation. The end of the * content entity is delineated by closing the underlying connection * (EOF condition). Entities transferred using this input stream can be of * unlimited length. *

* This decoder is optimized to transfer data directly from the underlying * I/O session's channel to a {@link FileChannel}, whenever * possible avoiding intermediate buffering in the session buffer. * * @since 4.0 */ public class IdentityDecoder extends AbstractContentDecoder implements FileContentDecoder { public IdentityDecoder( final ReadableByteChannel channel, final SessionInputBuffer buffer, final BasicHttpTransportMetrics metrics) { super(channel, buffer, metrics); } @Override public int read(final ByteBuffer dst) throws IOException { Args.notNull(dst, "Byte buffer"); if (isCompleted()) { return -1; } final int bytesRead; if (this.buffer.hasData()) { bytesRead = this.buffer.read(dst); } else { bytesRead = readFromChannel(dst); } if (bytesRead == -1) { setCompleted(); } return bytesRead; } @Override public long transfer( final FileChannel dst, final long position, final long count) throws IOException { if (dst == null) { return 0; } if (isCompleted()) { return 0; } long bytesRead; if (this.buffer.hasData()) { final int maxLen = this.buffer.length(); dst.position(position); bytesRead = this.buffer.read(dst, count < maxLen ? (int)count : maxLen); } else { if (this.channel.isOpen()) { if (position > dst.size()) { throw new IOException("Position past end of file [" + position + " > " + dst.size() + "]"); } bytesRead = dst.transferFrom(this.channel, position, count); if (count > 0 && bytesRead == 0) { bytesRead = this.buffer.fill(this.channel); } } else { bytesRead = -1; } if (bytesRead > 0) { this.metrics.incrementBytesTransferred(bytesRead); } } if (bytesRead == -1) { setCompleted(); } return bytesRead; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("[identity; completed: "); sb.append(this.completed); sb.append("]"); return sb.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpRequestParserFactory.java0100664 0000000 0000000 00000005765 14245617503 031475 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestFactory; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.LazyLineParser; import org.apache.hc.core5.http.message.LineParser; import org.apache.hc.core5.http.nio.NHttpMessageParser; import org.apache.hc.core5.http.nio.NHttpMessageParserFactory; /** * Default factory for request message parsers. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultHttpRequestParserFactory implements NHttpMessageParserFactory { public static final DefaultHttpRequestParserFactory INSTANCE = new DefaultHttpRequestParserFactory(); private final Http1Config http1Config; private final LineParser lineParser; private final HttpRequestFactory requestFactory; public DefaultHttpRequestParserFactory( final Http1Config http1Config, final HttpRequestFactory requestFactory, final LineParser lineParser) { super(); this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.requestFactory = requestFactory != null ? requestFactory : DefaultHttpRequestFactory.INSTANCE; this.lineParser = lineParser != null ? lineParser : LazyLineParser.INSTANCE; } public DefaultHttpRequestParserFactory(final Http1Config http1Config) { this(http1Config, null, null); } public DefaultHttpRequestParserFactory() { this(null); } @Override public NHttpMessageParser create() { return new DefaultHttpRequestParser<>(requestFactory, lineParser, http1Config); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpRequestWriter.java0100664 0000000 0000000 00000005214 14245617503 030152 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.LineFormatter; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.util.CharArrayBuffer; /** * Default {@link org.apache.hc.core5.http.nio.NHttpMessageWriter} implementation for {@link HttpRequest}s. * * @since 4.1 */ public class DefaultHttpRequestWriter extends AbstractMessageWriter { /** * Creates an instance of DefaultHttpRequestWriter. * * @param formatter the line formatter If {@code null} * {@link org.apache.hc.core5.http.message.BasicLineFormatter#INSTANCE} will be used. * * @since 4.3 */ public DefaultHttpRequestWriter(final LineFormatter formatter) { super(formatter); } /** * @since 4.3 */ public DefaultHttpRequestWriter() { super(null); } @Override protected void writeHeadLine(final T message, final CharArrayBuffer lineBuf) throws IOException { lineBuf.clear(); final ProtocolVersion transportVersion = message.getVersion(); getLineFormatter().formatRequestLine(lineBuf, new RequestLine( message.getMethod(), message.getRequestUri(), transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1)); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexerFactory.java0100664 0000000 0000000 00000014350 14245617503 031535 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.nio.NHttpMessageParserFactory; import org.apache.hc.core5.http.nio.NHttpMessageWriterFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; /** * {@link ClientHttp1StreamDuplexer} factory. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) @Internal public final class ClientHttp1StreamDuplexerFactory { private final HttpProcessor httpProcessor; private final Http1Config http1Config; private final CharCodingConfig charCodingConfig; private final ConnectionReuseStrategy connectionReuseStrategy; private final NHttpMessageParserFactory responseParserFactory; private final NHttpMessageWriterFactory requestWriterFactory; private final ContentLengthStrategy incomingContentStrategy; private final ContentLengthStrategy outgoingContentStrategy; private final Http1StreamListener streamListener; public ClientHttp1StreamDuplexerFactory( final HttpProcessor httpProcessor, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ConnectionReuseStrategy connectionReuseStrategy, final NHttpMessageParserFactory responseParserFactory, final NHttpMessageWriterFactory requestWriterFactory, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final Http1StreamListener streamListener) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.connectionReuseStrategy = connectionReuseStrategy != null ? connectionReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE; this.responseParserFactory = responseParserFactory != null ? responseParserFactory : new DefaultHttpResponseParserFactory(http1Config); this.requestWriterFactory = requestWriterFactory != null ? requestWriterFactory : DefaultHttpRequestWriterFactory.INSTANCE; this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy : DefaultContentLengthStrategy.INSTANCE; this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy : DefaultContentLengthStrategy.INSTANCE; this.streamListener = streamListener; } public ClientHttp1StreamDuplexerFactory( final HttpProcessor httpProcessor, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ConnectionReuseStrategy connectionReuseStrategy, final NHttpMessageParserFactory responseParserFactory, final NHttpMessageWriterFactory requestWriterFactory, final Http1StreamListener streamListener) { this(httpProcessor, http1Config, charCodingConfig, connectionReuseStrategy, responseParserFactory, requestWriterFactory, null, null, streamListener); } public ClientHttp1StreamDuplexerFactory( final HttpProcessor httpProcessor, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final Http1StreamListener streamListener) { this(httpProcessor, http1Config, charCodingConfig, null, null, null, streamListener); } public ClientHttp1StreamDuplexerFactory( final HttpProcessor httpProcessor, final Http1Config http1Config, final CharCodingConfig charCodingConfig) { this(httpProcessor, http1Config, charCodingConfig, null); } public ClientHttp1StreamDuplexer create(final ProtocolIOSession ioSession) { return new ClientHttp1StreamDuplexer( ioSession, httpProcessor, http1Config, charCodingConfig, connectionReuseStrategy, responseParserFactory.create(), requestWriterFactory.create(), incomingContentStrategy, outgoingContentStrategy, streamListener); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/IdentityEncoder.java0100664 0000000 0000000 00000012201 14403631147 026237 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.WritableByteChannel; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.FileContentEncoder; import org.apache.hc.core5.http.nio.SessionOutputBuffer; /** * Content encoder that writes data without any transformation. The end of * the content entity is demarcated by closing the underlying connection * (EOF condition). Entities transferred using this input stream can be of * unlimited length. *

* This decoder is optimized to transfer data directly from * a {@link FileChannel} to the underlying I/O session's channel whenever * possible avoiding intermediate buffering in the session buffer. * * @since 4.0 */ public class IdentityEncoder extends AbstractContentEncoder implements FileContentEncoder { private final int fragHint; /** * @since 4.3 * * @param channel underlying channel. * @param buffer session buffer. * @param metrics transport metrics. * @param chunkSizeHint fragment size hint defining an minimal size of a fragment * that should be written out directly to the channel bypassing the session buffer. * Value {@code 0} disables fragment buffering. */ public IdentityEncoder( final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics, final int chunkSizeHint) { super(channel, buffer, metrics); this.fragHint = Math.max(chunkSizeHint, 0); } public IdentityEncoder( final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics) { this(channel, buffer, metrics, 0); } @Override public int write(final ByteBuffer src) throws IOException { if (src == null) { return 0; } assertNotCompleted(); int total = 0; while (src.hasRemaining()) { if (this.buffer.hasData() || this.fragHint > 0) { if (src.remaining() <= this.fragHint) { final int capacity = this.fragHint - this.buffer.length(); if (capacity > 0) { final int limit = Math.min(capacity, src.remaining()); final int bytesWritten = writeToBuffer(src, limit); total += bytesWritten; } } } if (this.buffer.hasData()) { if (this.buffer.length() >= this.fragHint || src.hasRemaining()) { final int bytesWritten = flushToChannel(); if (bytesWritten == 0) { break; } } } if (!this.buffer.hasData() && src.remaining() > this.fragHint) { final int bytesWritten = writeToChannel(src); total += bytesWritten; if (bytesWritten == 0) { break; } } } return total; } @Override public long transfer( final FileChannel src, final long position, final long count) throws IOException { if (src == null) { return 0; } assertNotCompleted(); flushToChannel(); if (this.buffer.hasData()) { return 0; } final long bytesWritten = src.transferTo(position, count, this.channel); if (bytesWritten > 0) { this.metrics.incrementBytesTransferred(bytesWritten); } return bytesWritten; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("[identity; completed: "); sb.append(isCompleted()); sb.append("]"); return sb.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/LengthDelimitedDecoder.java0100664 0000000 0000000 00000013525 14245617503 027514 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.FileContentDecoder; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.apache.hc.core5.util.Args; /** * Content decoder that cuts off after a defined number of bytes. This class * is used to receive content of HTTP messages where the end of the content * entity is determined by the value of the {@code Content-Length header}. * Entities transferred using this stream can be maximum {@link Long#MAX_VALUE} * long. *

* This decoder is optimized to transfer data directly from the underlying * I/O session's channel to a {@link FileChannel}, whenever * possible avoiding intermediate buffering in the session buffer. * * @since 4.0 */ public class LengthDelimitedDecoder extends AbstractContentDecoder implements FileContentDecoder { private final long contentLength; private long len; public LengthDelimitedDecoder( final ReadableByteChannel channel, final SessionInputBuffer buffer, final BasicHttpTransportMetrics metrics, final long contentLength) { super(channel, buffer, metrics); Args.notNegative(contentLength, "Content length"); this.contentLength = contentLength; } @Override public int read(final ByteBuffer dst) throws IOException { Args.notNull(dst, "Byte buffer"); if (isCompleted()) { return -1; } final int chunk = (int) Math.min((this.contentLength - this.len), Integer.MAX_VALUE); final int bytesRead; if (this.buffer.hasData()) { final int maxLen = Math.min(chunk, this.buffer.length()); bytesRead = this.buffer.read(dst, maxLen); } else { bytesRead = readFromChannel(dst, chunk); } if (bytesRead == -1) { setCompleted(); if (this.len < this.contentLength) { throw new ConnectionClosedException( "Premature end of Content-Length delimited message body (expected: %d; received: %d)", this.contentLength, this.len); } } this.len += bytesRead; if (this.len >= this.contentLength) { this.completed = true; } if (this.completed && bytesRead == 0) { return -1; } return bytesRead; } @Override public long transfer( final FileChannel dst, final long position, final long count) throws IOException { if (dst == null) { return 0; } if (isCompleted()) { return -1; } final int chunk = (int) Math.min((this.contentLength - this.len), Integer.MAX_VALUE); final long bytesRead; if (this.buffer.hasData()) { final int maxLen = Math.min(chunk, this.buffer.length()); dst.position(position); bytesRead = this.buffer.read(dst, count < maxLen ? (int)count : maxLen); } else { if (this.channel.isOpen()) { if (position > dst.size()) { throw new IOException(String.format("Position past end of file [%d > %d]", position, dst.size())); } bytesRead = dst.transferFrom(this.channel, position, count < chunk ? count : chunk); } else { bytesRead = -1; } if (bytesRead > 0) { this.metrics.incrementBytesTransferred(bytesRead); } } if (bytesRead == -1) { setCompleted(); if (this.len < this.contentLength) { throw new ConnectionClosedException( "Premature end of Content-Length delimited message body (expected: %d; received: %d)", this.contentLength, this.len); } } this.len += bytesRead; if (this.len >= this.contentLength) { this.completed = true; } return bytesRead; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("[content length: "); sb.append(this.contentLength); sb.append("; pos: "); sb.append(this.len); sb.append("; completed: "); sb.append(this.completed); sb.append("]"); return sb.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1IOEventHandler.java0100664 0000000 0000000 00000004550 14323605260 030073 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.net.InetAddressUtils; /** * {@link org.apache.hc.core5.reactor.IOEventHandler} that implements * server side HTTP/1.1 messaging protocol with full support for * duplexed message transmission and message pipelining. * * @since 5.0 */ public class ServerHttp1IOEventHandler extends AbstractHttp1IOEventHandler { public ServerHttp1IOEventHandler(final ServerHttp1StreamDuplexer streamDuplexer) { super(streamDuplexer); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); InetAddressUtils.formatAddress(buf, getRemoteAddress()); buf.append("->"); InetAddressUtils.formatAddress(buf, getLocalAddress()); buf.append(" ["); streamDuplexer.appendState(buf); buf.append("]"); return buf.toString(); } @Override public ProtocolVersion getProtocolVersion() { final ProtocolVersion protocolVersion = super.getProtocolVersion(); return protocolVersion != null ? protocolVersion : HttpVersion.HTTP_1_1; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java0100664 0000000 0000000 00000030036 14403631147 027765 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.UnsupportedHttpVersionException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResourceHolder; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.util.Timeout; class ClientHttp1StreamHandler implements ResourceHolder { private final Http1StreamChannel outputChannel; private final DataStreamChannel internalDataChannel; private final HttpProcessor httpProcessor; private final Http1Config http1Config; private final ConnectionReuseStrategy connectionReuseStrategy; private final AsyncClientExchangeHandler exchangeHandler; private final HttpCoreContext context; private final AtomicBoolean requestCommitted; private final AtomicBoolean done; private volatile boolean keepAlive; private volatile Timeout timeout; private volatile HttpRequest committedRequest; private volatile MessageState requestState; private volatile MessageState responseState; ClientHttp1StreamHandler( final Http1StreamChannel outputChannel, final HttpProcessor httpProcessor, final Http1Config http1Config, final ConnectionReuseStrategy connectionReuseStrategy, final AsyncClientExchangeHandler exchangeHandler, final HttpCoreContext context) { this.outputChannel = outputChannel; this.internalDataChannel = new DataStreamChannel() { @Override public void requestOutput() { outputChannel.requestOutput(); } @Override public void endStream(final List trailers) throws IOException { outputChannel.complete(trailers); requestState = MessageState.COMPLETE; } @Override public int write(final ByteBuffer src) throws IOException { return outputChannel.write(src); } @Override public void endStream() throws IOException { endStream(null); } }; this.httpProcessor = httpProcessor; this.http1Config = http1Config; this.connectionReuseStrategy = connectionReuseStrategy; this.exchangeHandler = exchangeHandler; this.context = context; this.requestCommitted = new AtomicBoolean(false); this.done = new AtomicBoolean(false); this.keepAlive = true; this.requestState = MessageState.IDLE; this.responseState = MessageState.HEADERS; } boolean isResponseFinal() { return responseState == MessageState.COMPLETE; } boolean isCompleted() { return requestState == MessageState.COMPLETE && responseState == MessageState.COMPLETE; } String getRequestMethod() { return committedRequest != null ? committedRequest.getMethod() : null; } boolean isOutputReady() { switch (requestState) { case IDLE: case ACK: return true; case BODY: return exchangeHandler.available() > 0; default: return false; } } private void commitRequest(final HttpRequest request, final EntityDetails entityDetails) throws IOException, HttpException { if (requestCommitted.compareAndSet(false, true)) { final ProtocolVersion transportVersion = request.getVersion(); if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) { throw new UnsupportedHttpVersionException(transportVersion); } context.setProtocolVersion(transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); httpProcessor.process(request, entityDetails, context); final boolean endStream = entityDetails == null; if (endStream) { outputChannel.submit(request, true, FlushMode.IMMEDIATE); committedRequest = request; requestState = MessageState.COMPLETE; } else { final Header h = request.getFirstHeader(HttpHeaders.EXPECT); final boolean expectContinue = h != null && HeaderElements.CONTINUE.equalsIgnoreCase(h.getValue()); outputChannel.submit(request, false, expectContinue ? FlushMode.IMMEDIATE : FlushMode.BUFFER); committedRequest = request; if (expectContinue) { requestState = MessageState.ACK; timeout = outputChannel.getSocketTimeout(); outputChannel.setSocketTimeout(http1Config.getWaitForContinueTimeout()); } else { requestState = MessageState.BODY; exchangeHandler.produce(internalDataChannel); } } } else { throw new HttpException("Request already committed"); } } void produceOutput() throws HttpException, IOException { switch (requestState) { case IDLE: requestState = MessageState.HEADERS; exchangeHandler.produceRequest((request, entityDetails, httpContext) -> commitRequest(request, entityDetails), context); break; case ACK: outputChannel.suspendOutput(); break; case BODY: exchangeHandler.produce(internalDataChannel); break; } } void consumeHeader(final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException { if (done.get() || responseState != MessageState.HEADERS) { throw new ProtocolException("Unexpected message head"); } final ProtocolVersion transportVersion = response.getVersion(); if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) { throw new UnsupportedHttpVersionException(transportVersion); } final int status = response.getCode(); if (status < HttpStatus.SC_INFORMATIONAL) { throw new ProtocolException("Invalid response: " + new StatusLine(response)); } if (status > HttpStatus.SC_CONTINUE && status < HttpStatus.SC_SUCCESS) { exchangeHandler.consumeInformation(response, context); } else { if (!connectionReuseStrategy.keepAlive(committedRequest, response, context)) { keepAlive = false; } } if (requestState == MessageState.ACK) { if (status == HttpStatus.SC_CONTINUE || status >= HttpStatus.SC_SUCCESS) { outputChannel.setSocketTimeout(timeout); requestState = MessageState.BODY; if (status < HttpStatus.SC_CLIENT_ERROR) { exchangeHandler.produce(internalDataChannel); } } } if (status < HttpStatus.SC_SUCCESS) { return; } if (requestState == MessageState.BODY) { if (status >= HttpStatus.SC_CLIENT_ERROR) { requestState = MessageState.COMPLETE; if (!outputChannel.abortGracefully()) { keepAlive = false; } } } context.setProtocolVersion(transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1); context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); httpProcessor.process(response, entityDetails, context); if (entityDetails == null && !keepAlive) { outputChannel.close(); } exchangeHandler.consumeResponse(response, entityDetails, context); if (entityDetails == null) { responseState = MessageState.COMPLETE; } else { responseState = MessageState.BODY; } } void consumeData(final ByteBuffer src) throws HttpException, IOException { if (done.get() || responseState != MessageState.BODY) { throw new ProtocolException("Unexpected message data"); } exchangeHandler.consume(src); } void updateCapacity(final CapacityChannel capacityChannel) throws IOException { exchangeHandler.updateCapacity(capacityChannel); } void dataEnd(final List trailers) throws HttpException, IOException { if (done.get() || responseState != MessageState.BODY) { throw new ProtocolException("Unexpected message data"); } if (!keepAlive) { outputChannel.close(); } responseState = MessageState.COMPLETE; exchangeHandler.streamEnd(trailers); } boolean handleTimeout() { if (requestState == MessageState.ACK) { requestState = MessageState.BODY; outputChannel.setSocketTimeout(timeout); outputChannel.requestOutput(); return true; } return false; } void failed(final Exception cause) { if (!done.get()) { exchangeHandler.failed(cause); } } @Override public void releaseResources() { if (done.compareAndSet(false, true)) { responseState = MessageState.COMPLETE; requestState = MessageState.COMPLETE; exchangeHandler.releaseResources(); } } void appendState(final StringBuilder buf) { buf.append("requestState=").append(requestState) .append(", responseState=").append(responseState) .append(", responseCommitted=").append(requestCommitted) .append(", keepAlive=").append(keepAlive) .append(", done=").append(done); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append("["); appendState(buf); buf.append("]"); return buf.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1IOEventHandlerFactory.java0100664 0000000 0000000 00000005776 14403631147 031410 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.reactor.EndpointParameters; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * {@link ClientHttp1IOEventHandler} factory. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class ClientHttp1IOEventHandlerFactory implements IOEventHandlerFactory { private final ClientHttp1StreamDuplexerFactory streamDuplexerFactory; private final TlsStrategy tlsStrategy; private final Timeout handshakeTimeout; public ClientHttp1IOEventHandlerFactory( final ClientHttp1StreamDuplexerFactory streamDuplexerFactory, final TlsStrategy tlsStrategy, final Timeout handshakeTimeout) { this.streamDuplexerFactory = Args.notNull(streamDuplexerFactory, "Stream duplexer factory"); this.tlsStrategy = tlsStrategy; this.handshakeTimeout = handshakeTimeout; } @Override public IOEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { if (attachment instanceof EndpointParameters) { final EndpointParameters params = (EndpointParameters) attachment; if (tlsStrategy != null && URIScheme.HTTPS.same(params.getScheme())) { tlsStrategy.upgrade(ioSession, params, params.getAttachment(), handshakeTimeout, null); } } return new ClientHttp1IOEventHandler(streamDuplexerFactory.create(ioSession)); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/BasicHttpConnectionMetrics.java0100664 0000000 0000000 00000005603 14245617503 027625 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.http.HttpConnectionMetrics; import org.apache.hc.core5.http.io.HttpTransportMetrics; /** * Default implementation of the {@link HttpConnectionMetrics} interface. * * @since 4.0 */ public final class BasicHttpConnectionMetrics implements HttpConnectionMetrics { private final HttpTransportMetrics inTransportMetric; private final HttpTransportMetrics outTransportMetric; private final AtomicLong requestCount; private final AtomicLong responseCount; public BasicHttpConnectionMetrics( final HttpTransportMetrics inTransportMetric, final HttpTransportMetrics outTransportMetric) { super(); this.inTransportMetric = inTransportMetric; this.outTransportMetric = outTransportMetric; this.requestCount = new AtomicLong(0); this.responseCount = new AtomicLong(0); } @Override public long getReceivedBytesCount() { if (this.inTransportMetric != null) { return this.inTransportMetric.getBytesTransferred(); } return -1; } @Override public long getSentBytesCount() { if (this.outTransportMetric != null) { return this.outTransportMetric.getBytesTransferred(); } return -1; } @Override public long getRequestCount() { return this.requestCount.get(); } public void incrementRequestCount() { this.requestCount.incrementAndGet(); } @Override public long getResponseCount() { return this.responseCount.get(); } public void incrementResponseCount() { this.responseCount.incrementAndGet(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/CharCodingSupport.java0100664 0000000 0000000 00000005465 14245617503 026001 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CodingErrorAction; import org.apache.hc.core5.http.config.CharCodingConfig; public final class CharCodingSupport { private CharCodingSupport() { } public static CharsetDecoder createDecoder(final CharCodingConfig cconfig) { if (cconfig == null) { return null; } final Charset charset = cconfig.getCharset(); final CodingErrorAction malformed = cconfig.getMalformedInputAction(); final CodingErrorAction unmappable = cconfig.getUnmappableInputAction(); if (charset != null) { return charset.newDecoder() .onMalformedInput(malformed != null ? malformed : CodingErrorAction.REPORT) .onUnmappableCharacter(unmappable != null ? unmappable: CodingErrorAction.REPORT); } return null; } public static CharsetEncoder createEncoder(final CharCodingConfig cconfig) { if (cconfig == null) { return null; } final Charset charset = cconfig.getCharset(); if (charset != null) { final CodingErrorAction malformed = cconfig.getMalformedInputAction(); final CodingErrorAction unmappable = cconfig.getUnmappableInputAction(); return charset.newEncoder() .onMalformedInput(malformed != null ? malformed : CodingErrorAction.REPORT) .onUnmappableCharacter(unmappable != null ? unmappable: CodingErrorAction.REPORT); } return null; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/BasicHttpTransportMetrics.java0100664 0000000 0000000 00000003504 14245617503 027520 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.http.io.HttpTransportMetrics; /** * Default implementation of {@link HttpTransportMetrics}. * * @since 4.0 */ public class BasicHttpTransportMetrics implements HttpTransportMetrics { private final AtomicLong bytesTransferred; public BasicHttpTransportMetrics() { this.bytesTransferred = new AtomicLong(0); } @Override public long getBytesTransferred() { return this.bytesTransferred.get(); } public void incrementBytesTransferred(final long count) { this.bytesTransferred.addAndGet(count); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/0040775 0000000 0000000 00000000000 14435411677 022145 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/package-info.java0100664 0000000 0000000 00000002461 14245617503 025327 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Default implementation of HTTP/1.1 transport based on the classic * (blocking) I/O model. */ package org.apache.hc.core5.http.impl.io; httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/ChunkedOutputStream.java0100664 0000000 0000000 00000021677 14315123013 026755 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.OutputStream; import java.util.List; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.FormattedHeader; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.StreamClosedException; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.apache.hc.core5.http.message.BasicLineFormatter; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Implements chunked transfer coding. The content is sent in small chunks. * Entities transferred using this output stream can be of unlimited length. * Writes are buffered to an internal buffer (2048 default size). *

* Note that this class NEVER closes the underlying stream, even when * {@link #close()} gets called. Instead, the stream will be marked as * closed and no further output will be permitted. * * * @since 4.0 */ public class ChunkedOutputStream extends OutputStream { private final SessionOutputBuffer buffer; private final OutputStream outputStream; private final byte[] cache; private int cachePosition; private boolean wroteLastChunk; private boolean closed; private final CharArrayBuffer lineBuffer; private final Supplier> trailerSupplier; /** * Default constructor. * * @param buffer Session output buffer * @param outputStream Output stream * @param chunkCache Buffer used to aggregate smaller writes into chunks. * @param trailerSupplier Trailer supplier. May be {@code null} * * @since 5.1 */ public ChunkedOutputStream( final SessionOutputBuffer buffer, final OutputStream outputStream, final byte[] chunkCache, final Supplier> trailerSupplier) { super(); this.buffer = Args.notNull(buffer, "Session output buffer"); this.outputStream = Args.notNull(outputStream, "Output stream"); this.cache = Args.notNull(chunkCache, "Chunk cache"); this.lineBuffer = new CharArrayBuffer(32); this.trailerSupplier = trailerSupplier; } /** * Constructor taking an integer chunk size hint. * * @param buffer Session output buffer * @param outputStream Output stream * @param chunkSizeHint minimal chunk size hint * @param trailerSupplier Trailer supplier. May be {@code null} * * @since 5.0 */ public ChunkedOutputStream( final SessionOutputBuffer buffer, final OutputStream outputStream, final int chunkSizeHint, final Supplier> trailerSupplier) { this(buffer, outputStream, new byte[chunkSizeHint > 0 ? chunkSizeHint : 8192], trailerSupplier); } /** * Constructor with no trailers. * * @param buffer Session output buffer * @param outputStream Output stream * @param chunkSizeHint minimal chunk size hint */ public ChunkedOutputStream(final SessionOutputBuffer buffer, final OutputStream outputStream, final int chunkSizeHint) { this(buffer, outputStream, chunkSizeHint, null); } /** * Writes the cache out onto the underlying stream */ private void flushCache() throws IOException { if (this.cachePosition > 0) { this.lineBuffer.clear(); this.lineBuffer.append(Integer.toHexString(this.cachePosition)); this.buffer.writeLine(this.lineBuffer, this.outputStream); this.buffer.write(this.cache, 0, this.cachePosition, this.outputStream); this.lineBuffer.clear(); this.buffer.writeLine(this.lineBuffer, this.outputStream); this.cachePosition = 0; } } /** * Writes the cache and bufferToAppend to the underlying stream * as one large chunk */ private void flushCacheWithAppend(final byte[] bufferToAppend, final int off, final int len) throws IOException { this.lineBuffer.clear(); this.lineBuffer.append(Integer.toHexString(this.cachePosition + len)); this.buffer.writeLine(this.lineBuffer, this.outputStream); this.buffer.write(this.cache, 0, this.cachePosition, this.outputStream); this.buffer.write(bufferToAppend, off, len, this.outputStream); this.lineBuffer.clear(); this.buffer.writeLine(this.lineBuffer, this.outputStream); this.cachePosition = 0; } private void writeClosingChunk() throws IOException { // Write the final chunk. this.lineBuffer.clear(); this.lineBuffer.append('0'); this.buffer.writeLine(this.lineBuffer, this.outputStream); writeTrailers(); this.lineBuffer.clear(); this.buffer.writeLine(this.lineBuffer, this.outputStream); } private void writeTrailers() throws IOException { final List trailers = this.trailerSupplier != null ? this.trailerSupplier.get() : null; if (trailers != null) { for (int i = 0; i < trailers.size(); i++) { final Header header = trailers.get(i); if (header instanceof FormattedHeader) { final CharArrayBuffer chbuffer = ((FormattedHeader) header).getBuffer(); this.buffer.writeLine(chbuffer, this.outputStream); } else { this.lineBuffer.clear(); BasicLineFormatter.INSTANCE.formatHeader(this.lineBuffer, header); this.buffer.writeLine(this.lineBuffer, this.outputStream); } } } } // ----------------------------------------------------------- Public Methods /** * Must be called to ensure the internal cache is flushed and the closing * chunk is written. * @throws IOException in case of an I/O error */ public void finish() throws IOException { if (!this.wroteLastChunk) { flushCache(); writeClosingChunk(); this.wroteLastChunk = true; } } // -------------------------------------------- OutputStream Methods @Override public void write(final int b) throws IOException { if (this.closed) { throw new StreamClosedException(); } this.cache[this.cachePosition] = (byte) b; this.cachePosition++; if (this.cachePosition == this.cache.length) { flushCache(); } } /** * Writes the array. If the array does not fit within the buffer, it is * not split, but rather written out as one large chunk. */ @Override public void write(final byte[] b) throws IOException { write(b, 0, b.length); } /** * Writes the array. If the array does not fit within the buffer, it is * not split, but rather written out as one large chunk. */ @Override public void write(final byte[] src, final int off, final int len) throws IOException { if (this.closed) { throw new StreamClosedException(); } if (len >= this.cache.length - this.cachePosition) { flushCacheWithAppend(src, off, len); } else { System.arraycopy(src, off, cache, this.cachePosition, len); this.cachePosition += len; } } /** * Flushes the content buffer and the underlying stream. */ @Override public void flush() throws IOException { flushCache(); this.buffer.flush(this.outputStream); } /** * Finishes writing to the underlying stream, but does NOT close the underlying stream. */ @Override public void close() throws IOException { if (!this.closed) { this.closed = true; finish(); this.buffer.flush(this.outputStream); } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpResponseWriterFactory.java0100664 0000000 0000000 00000004615 14245617503 031476 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.io.HttpMessageWriter; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; import org.apache.hc.core5.http.message.BasicLineFormatter; import org.apache.hc.core5.http.message.LineFormatter; /** * Default factory for response message writers. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultHttpResponseWriterFactory implements HttpMessageWriterFactory { public static final DefaultHttpResponseWriterFactory INSTANCE = new DefaultHttpResponseWriterFactory(); private final LineFormatter lineFormatter; public DefaultHttpResponseWriterFactory(final LineFormatter lineFormatter) { super(); this.lineFormatter = lineFormatter != null ? lineFormatter : BasicLineFormatter.INSTANCE; } public DefaultHttpResponseWriterFactory() { this(null); } @Override public HttpMessageWriter create() { return new DefaultHttpResponseWriter(this.lineFormatter); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java0100664 0000000 0000000 00000034501 14403631147 025237 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequestMapper; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.UnsupportedHttpVersionException; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.ServerSupport; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.HttpServerConnection; import org.apache.hc.core5.http.io.HttpServerRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.io.support.BasicHttpServerExpectationDecorator; import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.util.Args; /** * {@code HttpService} is a server side HTTP protocol handler based on * the classic (blocking) I/O model. *

* {@code HttpService} relies on {@link HttpProcessor} to generate mandatory * protocol headers for all outgoing messages and apply common, cross-cutting * message transformations to all incoming and outgoing messages, whereas * individual {@link HttpRequestHandler}s are expected to implement * application specific content generation and processing. *

* {@code HttpService} uses {@link HttpRequestMapper} to map * matching request handler for a particular request URI of an incoming HTTP * request. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class HttpService { private final HttpProcessor processor; private final HttpServerRequestHandler requestHandler; private final ConnectionReuseStrategy connReuseStrategy; private final Http1StreamListener streamListener; /** * Create a new HTTP service. * * @param processor the processor to use on requests and responses * @param handlerMapper the handler mapper * @param responseFactory the response factory. If {@code null} * {@link DefaultClassicHttpResponseFactory#INSTANCE} will be used. * @param connReuseStrategy the connection reuse strategy. If {@code null} * {@link DefaultConnectionReuseStrategy#INSTANCE} will be used. * @param streamListener message stream listener. */ public HttpService( final HttpProcessor processor, final HttpRequestMapper handlerMapper, final ConnectionReuseStrategy connReuseStrategy, final HttpResponseFactory responseFactory, final Http1StreamListener streamListener) { this(processor, new BasicHttpServerExpectationDecorator(new BasicHttpServerRequestHandler(handlerMapper, responseFactory)), connReuseStrategy, streamListener); } /** * Create a new HTTP service. * * @param processor the processor to use on requests and responses * @param handlerMapper the handler mapper * @param connReuseStrategy the connection reuse strategy. If {@code null} * {@link DefaultConnectionReuseStrategy#INSTANCE} will be used. * @param responseFactory the response factory. If {@code null} * {@link DefaultClassicHttpResponseFactory#INSTANCE} will be used. */ public HttpService( final HttpProcessor processor, final HttpRequestMapper handlerMapper, final ConnectionReuseStrategy connReuseStrategy, final HttpResponseFactory responseFactory) { this(processor, handlerMapper, connReuseStrategy, responseFactory, null); } /** * Create a new HTTP service. * * @param processor the processor to use on requests and responses * @param requestHandler the request handler. * @param connReuseStrategy the connection reuse strategy. If {@code null} * {@link DefaultConnectionReuseStrategy#INSTANCE} will be used. * @param streamListener message stream listener. */ public HttpService( final HttpProcessor processor, final HttpServerRequestHandler requestHandler, final ConnectionReuseStrategy connReuseStrategy, final Http1StreamListener streamListener) { super(); this.processor = Args.notNull(processor, "HTTP processor"); this.requestHandler = Args.notNull(requestHandler, "Request handler"); this.connReuseStrategy = connReuseStrategy != null ? connReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE; this.streamListener = streamListener; } /** * Create a new HTTP service. * * @param processor the processor to use on requests and responses * @param requestHandler the request handler. */ public HttpService( final HttpProcessor processor, final HttpServerRequestHandler requestHandler) { this(processor, requestHandler, null, null); } /** * Handles receives one HTTP request over the given connection within the * given execution context and sends a response back to the client. * * @param conn the active connection to the client * @param context the actual execution context. * @throws IOException in case of an I/O error. * @throws HttpException in case of HTTP protocol violation or a processing * problem. */ public void handleRequest( final HttpServerConnection conn, final HttpContext context) throws IOException, HttpException { final AtomicBoolean responseSubmitted = new AtomicBoolean(false); try { final ClassicHttpRequest request = conn.receiveRequestHeader(); if (request == null) { conn.close(); return; } if (streamListener != null) { streamListener.onRequestHead(conn, request); } conn.receiveRequestEntity(request); final ProtocolVersion transportVersion = request.getVersion(); context.setProtocolVersion(transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1); context.setAttribute(HttpCoreContext.SSL_SESSION, conn.getSSLSession()); context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, conn.getEndpointDetails()); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); this.processor.process(request, request.getEntity(), context); this.requestHandler.handle(request, new HttpServerRequestHandler.ResponseTrigger() { @Override public void sendInformation(final ClassicHttpResponse response) throws HttpException, IOException { if (responseSubmitted.get()) { throw new HttpException("Response already submitted"); } if (response.getCode() >= HttpStatus.SC_SUCCESS) { throw new HttpException("Invalid intermediate response"); } if (streamListener != null) { streamListener.onResponseHead(conn, response); } conn.sendResponseHeader(response); conn.flush(); } @Override public void submitResponse(final ClassicHttpResponse response) throws HttpException, IOException { try { final ProtocolVersion transportVersion = response.getVersion(); if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) { throw new UnsupportedHttpVersionException(transportVersion); } ServerSupport.validateResponse(response, response.getEntity()); context.setProtocolVersion(transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1); context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); processor.process(response, response.getEntity(), context); responseSubmitted.set(true); conn.sendResponseHeader(response); if (streamListener != null) { streamListener.onResponseHead(conn, response); } if (MessageSupport.canResponseHaveBody(request.getMethod(), response)) { conn.sendResponseEntity(response); } // Make sure the request content is fully consumed EntityUtils.consume(request.getEntity()); final boolean keepAlive = connReuseStrategy.keepAlive(request, response, context); if (streamListener != null) { streamListener.onExchangeComplete(conn, keepAlive); } if (!keepAlive) { conn.close(); } conn.flush(); } finally { response.close(); } } }, context); } catch (final HttpException ex) { if (responseSubmitted.get()) { throw ex; } try (final ClassicHttpResponse errorResponse = new BasicClassicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR)) { handleException(ex, errorResponse); errorResponse.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); context.setAttribute(HttpCoreContext.HTTP_RESPONSE, errorResponse); this.processor.process(errorResponse, errorResponse.getEntity(), context); conn.sendResponseHeader(errorResponse); if (streamListener != null) { streamListener.onResponseHead(conn, errorResponse); } conn.sendResponseEntity(errorResponse); conn.close(); } } } /** * Handles the given exception and generates an HTTP response to be sent * back to the client to inform about the exceptional condition encountered * in the course of the request processing. * * @param ex the exception. * @param response the HTTP response. */ protected void handleException(final HttpException ex, final ClassicHttpResponse response) { response.setCode(toStatusCode(ex)); response.setEntity(new StringEntity(ServerSupport.toErrorMessage(ex), ContentType.TEXT_PLAIN)); } protected int toStatusCode(final Exception ex) { return ServerSupport.toStatusCode(ex); } /** * Create a new {@link Builder}. * * @since 5.2 */ public static Builder builder() { return new Builder(); } /** * Builder for {@link HttpService}. * * @since 5.2 */ public static final class Builder { private HttpProcessor processor; private HttpServerRequestHandler requestHandler; private ConnectionReuseStrategy connReuseStrategy; private Http1StreamListener streamListener; private Builder() {} public Builder withHttpProcessor(final HttpProcessor processor) { this.processor = processor; return this; } public Builder withHttpServerRequestHandler(final HttpServerRequestHandler requestHandler) { this.requestHandler = requestHandler; return this; } public Builder withConnectionReuseStrategy(final ConnectionReuseStrategy connReuseStrategy) { this.connReuseStrategy = connReuseStrategy; return this; } public Builder withHttp1StreamListener(final Http1StreamListener streamListener) { this.streamListener = streamListener; return this; } /** * Create a new HTTP service. * * @since 5.2 */ public HttpService build() { return new HttpService( processor, requestHandler, connReuseStrategy, streamListener); } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultClassicHttpResponseFactory.java0100664 0000000 0000000 00000005663 14245617503 031607 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.ReasonPhraseCatalog; import org.apache.hc.core5.http.impl.EnglishReasonPhraseCatalog; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.util.Args; /** * Default factory for creating {@link ClassicHttpResponse} objects. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultClassicHttpResponseFactory implements HttpResponseFactory { public static final DefaultClassicHttpResponseFactory INSTANCE = new DefaultClassicHttpResponseFactory(); private final ReasonPhraseCatalog reasonCatalog; /** * Creates a new response factory with the given catalog. * * @param catalog the catalog of reason phrases */ public DefaultClassicHttpResponseFactory(final ReasonPhraseCatalog catalog) { this.reasonCatalog = Args.notNull(catalog, "Reason phrase catalog"); } /** * Creates a new response factory with the default catalog. * The default catalog is {@link EnglishReasonPhraseCatalog}. */ public DefaultClassicHttpResponseFactory() { this(EnglishReasonPhraseCatalog.INSTANCE); } @Override public ClassicHttpResponse newHttpResponse(final int status, final String reasonPhrase) { return new BasicClassicHttpResponse(status, reasonPhrase); } @Override public ClassicHttpResponse newHttpResponse(final int status) { return new BasicClassicHttpResponse(status, this.reasonCatalog, null); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/SessionOutputBufferImpl.java0100664 0000000 0000000 00000021323 14315123013 027603 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import org.apache.hc.core5.http.Chars; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.io.HttpTransportMetrics; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; import org.apache.hc.core5.util.CharArrayBuffer; /** * Abstract base class for session output buffers that stream data to * an arbitrary {@link OutputStream}. This class buffers small chunks of * output data in an internal byte array for optimal output performance. *

* {@link #writeLine(CharArrayBuffer, OutputStream)} method of this class uses CR-LF * as a line delimiter. * * @since 4.3 */ public class SessionOutputBufferImpl implements SessionOutputBuffer { private static final byte[] CRLF = new byte[] {Chars.CR, Chars.LF}; private final BasicHttpTransportMetrics metrics; private final ByteArrayBuffer buffer; private final int fragmentSizeHint; private final CharsetEncoder encoder; private ByteBuffer bbuf; /** * Creates new instance of SessionOutputBufferImpl. * * @param metrics HTTP transport metrics. * @param bufferSize buffer size. Must be a positive number. * @param fragmentSizeHint fragment size hint defining a minimal size of a fragment * that should be written out directly to the socket bypassing the session buffer. * Value {@code 0} disables fragment buffering. * @param charEncoder charEncoder to be used for encoding HTTP protocol elements. * If {@code null} simple type cast will be used for char to byte conversion. */ public SessionOutputBufferImpl( final BasicHttpTransportMetrics metrics, final int bufferSize, final int fragmentSizeHint, final CharsetEncoder charEncoder) { super(); Args.positive(bufferSize, "Buffer size"); Args.notNull(metrics, "HTTP transport metrics"); this.metrics = metrics; this.buffer = new ByteArrayBuffer(bufferSize); this.fragmentSizeHint = fragmentSizeHint >= 0 ? fragmentSizeHint : bufferSize; this.encoder = charEncoder; } public SessionOutputBufferImpl(final int bufferSize) { this(new BasicHttpTransportMetrics(), bufferSize, bufferSize, null); } public SessionOutputBufferImpl(final int bufferSize, final CharsetEncoder encoder) { this(new BasicHttpTransportMetrics(), bufferSize, bufferSize, encoder); } @Override public int capacity() { return this.buffer.capacity(); } @Override public int length() { return this.buffer.length(); } @Override public int available() { return capacity() - length(); } private void flushBuffer(final OutputStream outputStream) throws IOException { final int len = this.buffer.length(); if (len > 0) { outputStream.write(this.buffer.array(), 0, len); this.buffer.clear(); this.metrics.incrementBytesTransferred(len); } } @Override public void flush(final OutputStream outputStream) throws IOException { Args.notNull(outputStream, "Output stream"); flushBuffer(outputStream); outputStream.flush(); } @Override public void write(final byte[] b, final int off, final int len, final OutputStream outputStream) throws IOException { if (b == null) { return; } Args.notNull(outputStream, "Output stream"); // Do not want to buffer large-ish chunks // if the byte array is larger then MIN_CHUNK_LIMIT // write it directly to the output stream if (len > this.fragmentSizeHint || len > this.buffer.capacity()) { // flush the buffer flushBuffer(outputStream); // write directly to the out stream outputStream.write(b, off, len); this.metrics.incrementBytesTransferred(len); } else { // Do not let the buffer grow unnecessarily final int freecapacity = this.buffer.capacity() - this.buffer.length(); if (len > freecapacity) { // flush the buffer flushBuffer(outputStream); } // buffer this.buffer.append(b, off, len); } } @Override public void write(final byte[] b, final OutputStream outputStream) throws IOException { if (b == null) { return; } write(b, 0, b.length, outputStream); } @Override public void write(final int b, final OutputStream outputStream) throws IOException { Args.notNull(outputStream, "Output stream"); if (this.fragmentSizeHint > 0) { if (this.buffer.isFull()) { flushBuffer(outputStream); } this.buffer.append(b); } else { flushBuffer(outputStream); outputStream.write(b); } } /** * Writes characters from the specified char array followed by a line * delimiter to this session buffer. *

* This method uses CR-LF as a line delimiter. * * @param charbuffer the buffer containing chars of the line. * @throws IOException if an I/O error occurs. */ @Override public void writeLine(final CharArrayBuffer charbuffer, final OutputStream outputStream) throws IOException { if (charbuffer == null) { return; } Args.notNull(outputStream, "Output stream"); if (this.encoder == null) { int off = 0; int remaining = charbuffer.length(); while (remaining > 0) { int chunk = this.buffer.capacity() - this.buffer.length(); chunk = Math.min(chunk, remaining); if (chunk > 0) { this.buffer.append(charbuffer, off, chunk); } if (this.buffer.isFull()) { flushBuffer(outputStream); } off += chunk; remaining -= chunk; } } else { final CharBuffer cbuf = CharBuffer.wrap(charbuffer.array(), 0, charbuffer.length()); writeEncoded(cbuf, outputStream); } write(CRLF, outputStream); } private void writeEncoded(final CharBuffer cbuf, final OutputStream outputStream) throws IOException { if (!cbuf.hasRemaining()) { return; } if (this.bbuf == null) { this.bbuf = ByteBuffer.allocate(1024); } this.encoder.reset(); while (cbuf.hasRemaining()) { final CoderResult result = this.encoder.encode(cbuf, this.bbuf, true); handleEncodingResult(result, outputStream); } final CoderResult result = this.encoder.flush(this.bbuf); handleEncodingResult(result, outputStream); this.bbuf.clear(); } private void handleEncodingResult(final CoderResult result, final OutputStream outputStream) throws IOException { if (result.isError()) { result.throwException(); } this.bbuf.flip(); while (this.bbuf.hasRemaining()) { write(this.bbuf.get(), outputStream); } this.bbuf.compact(); } @Override public HttpTransportMetrics getMetrics() { return this.metrics; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultBHttpClientConnectionFactory.java0100664 0000000 0000000 00000020634 14245617503 032042 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.net.Socket; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.CharCodingSupport; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; import org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy; /** * Default factory for {@link org.apache.hc.core5.http.io.HttpClientConnection}s. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultBHttpClientConnectionFactory implements HttpConnectionFactory { private final Http1Config http1Config; private final CharCodingConfig charCodingConfig; private final ContentLengthStrategy incomingContentStrategy; private final ContentLengthStrategy outgoingContentStrategy; private final ResponseOutOfOrderStrategy responseOutOfOrderStrategy; private final HttpMessageWriterFactory requestWriterFactory; private final HttpMessageParserFactory responseParserFactory; private DefaultBHttpClientConnectionFactory( final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final ResponseOutOfOrderStrategy responseOutOfOrderStrategy, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.incomingContentStrategy = incomingContentStrategy; this.outgoingContentStrategy = outgoingContentStrategy; this.responseOutOfOrderStrategy = responseOutOfOrderStrategy; this.requestWriterFactory = requestWriterFactory; this.responseParserFactory = responseParserFactory; } public DefaultBHttpClientConnectionFactory( final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { this( http1Config, charCodingConfig, incomingContentStrategy, outgoingContentStrategy, null, requestWriterFactory, responseParserFactory); } public DefaultBHttpClientConnectionFactory( final Http1Config http1Config, final CharCodingConfig charCodingConfig, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { this(http1Config, charCodingConfig, null, null, requestWriterFactory, responseParserFactory); } public DefaultBHttpClientConnectionFactory( final Http1Config http1Config, final CharCodingConfig charCodingConfig) { this(http1Config, charCodingConfig, null, null, null, null); } public DefaultBHttpClientConnectionFactory() { this(null, null, null, null, null, null); } @Override public DefaultBHttpClientConnection createConnection(final Socket socket) throws IOException { final DefaultBHttpClientConnection conn = new DefaultBHttpClientConnection( this.http1Config, CharCodingSupport.createDecoder(this.charCodingConfig), CharCodingSupport.createEncoder(this.charCodingConfig), this.incomingContentStrategy, this.outgoingContentStrategy, this.responseOutOfOrderStrategy, this.requestWriterFactory, this.responseParserFactory); conn.bind(socket); return conn; } /** * Create a new {@link Builder}. * * @since 5.1 */ public static Builder builder() { return new Builder(); } /** * Builder for {@link DefaultBHttpClientConnectionFactory}. * * @since 5.1 */ public static final class Builder { private Http1Config http1Config; private CharCodingConfig charCodingConfig; private ContentLengthStrategy incomingContentLengthStrategy; private ContentLengthStrategy outgoingContentLengthStrategy; private ResponseOutOfOrderStrategy responseOutOfOrderStrategy; private HttpMessageWriterFactory requestWriterFactory; private HttpMessageParserFactory responseParserFactory; private Builder() {} public Builder http1Config(final Http1Config http1Config) { this.http1Config = http1Config; return this; } public Builder charCodingConfig(final CharCodingConfig charCodingConfig) { this.charCodingConfig = charCodingConfig; return this; } public Builder incomingContentLengthStrategy(final ContentLengthStrategy incomingContentLengthStrategy) { this.incomingContentLengthStrategy = incomingContentLengthStrategy; return this; } public Builder outgoingContentLengthStrategy(final ContentLengthStrategy outgoingContentLengthStrategy) { this.outgoingContentLengthStrategy = outgoingContentLengthStrategy; return this; } public Builder responseOutOfOrderStrategy(final ResponseOutOfOrderStrategy responseOutOfOrderStrategy) { this.responseOutOfOrderStrategy = responseOutOfOrderStrategy; return this; } public Builder requestWriterFactory( final HttpMessageWriterFactory requestWriterFactory) { this.requestWriterFactory = requestWriterFactory; return this; } public Builder responseParserFactory( final HttpMessageParserFactory responseParserFactory) { this.responseParserFactory = responseParserFactory; return this; } public DefaultBHttpClientConnectionFactory build() { return new DefaultBHttpClientConnectionFactory( http1Config, charCodingConfig, incomingContentLengthStrategy, outgoingContentLengthStrategy, responseOutOfOrderStrategy, requestWriterFactory, responseParserFactory); } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/IncomingHttpEntity.java0100664 0000000 0000000 00000007756 14245617503 026617 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collections; import java.util.List; import java.util.Set; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.AbstractHttpEntity; import org.apache.hc.core5.http.io.entity.EmptyInputStream; import org.apache.hc.core5.io.Closer; class IncomingHttpEntity implements HttpEntity { private final InputStream content; private final long len; private final boolean chunked; private final Header contentType; private final Header contentEncoding; IncomingHttpEntity(final InputStream content, final long len, final boolean chunked, final Header contentType, final Header contentEncoding) { this.content = content; this.len = len; this.chunked = chunked; this.contentType = contentType; this.contentEncoding = contentEncoding; } @Override public boolean isRepeatable() { return false; } @Override public boolean isChunked() { return chunked; } @Override public long getContentLength() { return len; } @Override public String getContentType() { return contentType != null ? contentType.getValue() : null; } @Override public String getContentEncoding() { return contentEncoding != null ? contentEncoding.getValue() : null; } @Override public InputStream getContent() throws IOException, IllegalStateException { return content; } @Override public boolean isStreaming() { return content != null && content != EmptyInputStream.INSTANCE; } @Override public void writeTo(final OutputStream outStream) throws IOException { AbstractHttpEntity.writeTo(this, outStream); } @Override public Supplier> getTrailers() { return null; } @Override public Set getTrailerNames() { return Collections.emptySet(); } @Override public void close() throws IOException { Closer.close(content); } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append('['); sb.append("Content-Type: "); sb.append(getContentType()); sb.append(','); sb.append("Content-Encoding: "); sb.append(getContentEncoding()); sb.append(','); final long len = getContentLength(); if (len >= 0) { sb.append("Content-Length: "); sb.append(len); sb.append(','); } sb.append("Chunked: "); sb.append(isChunked()); sb.append(']'); return sb.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/EmptyInputStream.java0100664 0000000 0000000 00000005172 14245617503 026277 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.InputStream; /** * @since 4.4 * @deprecated Please use {@link org.apache.hc.core5.http.io.entity.EmptyInputStream} */ @Deprecated public final class EmptyInputStream extends InputStream { public static final EmptyInputStream INSTANCE = new EmptyInputStream(); private EmptyInputStream() { // noop. } /** * Returns {@code 0}. */ @Override public int available() { return 0; } /** * Noop. */ @Override public void close() { // noop. } /** * Noop. */ @SuppressWarnings("sync-override") @Override public void mark(final int readLimit) { // noop. } /** * Returns {@code true}. */ @Override public boolean markSupported() { return true; } /** * Returns {@code -1}. */ @Override public int read() { return -1; } /** * Returns {@code -1}. */ @Override public int read(final byte[] buf) { return -1; } /** * Returns {@code -1}. */ @Override public int read(final byte[] buf, final int off, final int len) { return -1; } /** * Noop. */ @SuppressWarnings("sync-override") @Override public void reset() { // noop } /** * Returns {@code 0}. */ @Override public long skip(final long n) { return 0L; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/ContentLengthInputStream.java0100664 0000000 0000000 00000017336 14245617503 027762 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.StreamClosedException; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.util.Args; /** * Input stream that cuts off after a defined number of bytes. This class * is used to receive content of HTTP messages where the end of the content * entity is determined by the value of the {@code Content-Length header}. * Entities transferred using this stream can be maximum {@link Long#MAX_VALUE} * long. *

* Note that this class NEVER closes the underlying stream, even when * {@link #close()} gets called. Instead, it will read until the "end" of * its limit on close, which allows for the seamless execution of subsequent * HTTP 1.1 requests, while not requiring the client to remember to read the * entire contents of the response. * * * @since 4.0 */ public class ContentLengthInputStream extends InputStream { private static final int BUFFER_SIZE = 2048; private final SessionInputBuffer buffer; private final InputStream inputStream; /** * The maximum number of bytes that can be read from the stream. Subsequent * read operations will return -1. */ private final long contentLength; /** The current position */ private long pos; /** True if the stream is closed. */ private boolean closed; /** * Default constructor. * * @param buffer Session input buffer * @param inputStream Input stream * @param contentLength The maximum number of bytes that can be read from * the stream. Subsequent read operations will return -1. */ public ContentLengthInputStream(final SessionInputBuffer buffer, final InputStream inputStream, final long contentLength) { super(); this.buffer = Args.notNull(buffer, "Session input buffer"); this.inputStream = Args.notNull(inputStream, "Input stream"); this.contentLength = Args.notNegative(contentLength, "Content length"); } /** *

Reads until the end of the known length of content.

* *

Does NOT close the underlying stream, but instead leaves it * primed to parse the next response.

* @throws IOException If an IO problem occurs. */ @Override public void close() throws IOException { if (!closed) { try { if (pos < contentLength) { final byte[] buffer = new byte[BUFFER_SIZE]; while (read(buffer) >= 0) { // keep reading } } } finally { // close after above so that we don't throw an exception trying // to read after closed! closed = true; } } } @Override public int available() throws IOException { final int len = this.buffer.length(); return Math.min(len, (int) (this.contentLength - this.pos)); } /** * Read the next byte from the stream * @return The next byte or -1 if the end of stream has been reached. * @throws IOException If an IO problem occurs * @see java.io.InputStream#read() */ @Override public int read() throws IOException { if (closed) { throw new StreamClosedException(); } if (pos >= contentLength) { return -1; } final int b = this.buffer.read(this.inputStream); if (b == -1) { if (pos < contentLength) { throw new ConnectionClosedException( "Premature end of Content-Length delimited message body (expected: %d; received: %d)", contentLength, pos); } } else { pos++; } return b; } /** * Does standard {@link InputStream#read(byte[], int, int)} behavior, but * also notifies the watcher when the contents have been consumed. * * @param b The byte array to fill. * @param off Start filling at this position. * @param len The number of bytes to attempt to read. * @return The number of bytes read, or -1 if the end of content has been * reached. * * @throws java.io.IOException Should an error occur on the wrapped stream. */ @Override public int read(final byte[] b, final int off, final int len) throws java.io.IOException { if (closed) { throw new StreamClosedException(); } if (pos >= contentLength) { return -1; } int chunk = len; if (pos + len > contentLength) { chunk = (int) (contentLength - pos); } final int count = this.buffer.read(b, off, chunk, this.inputStream); if (count == -1 && pos < contentLength) { throw new ConnectionClosedException( "Premature end of Content-Length delimited message body (expected: %d; received: %d)", contentLength, pos); } if (count > 0) { pos += count; } return count; } /** * Read more bytes from the stream. * @param b The byte array to put the new data in. * @return The number of bytes read into the buffer. * @throws IOException If an IO problem occurs * @see java.io.InputStream#read(byte[]) */ @Override public int read(final byte[] b) throws IOException { return read(b, 0, b.length); } /** * Skips and discards a number of bytes from the input stream. * @param n The number of bytes to skip. * @return The actual number of bytes skipped. ≤ 0 if no bytes * are skipped. * @throws IOException If an error occurs while skipping bytes. * @see InputStream#skip(long) */ @Override public long skip(final long n) throws IOException { if (n <= 0) { return 0; } final byte[] buffer = new byte[BUFFER_SIZE]; // make sure we don't skip more bytes than are // still available long remaining = Math.min(n, this.contentLength - this.pos); // skip and keep track of the bytes actually skipped long count = 0; while (remaining > 0) { final int readLen = read(buffer, 0, (int)Math.min(BUFFER_SIZE, remaining)); if (readLen == -1) { break; } count += readLen; remaining -= readLen; } return count; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/SessionInputBufferImpl.java0100664 0000000 0000000 00000034064 14403631147 027422 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharsetDecoder; import java.nio.charset.CoderResult; import org.apache.hc.core5.http.Chars; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.io.HttpTransportMetrics; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; import org.apache.hc.core5.util.CharArrayBuffer; /** * Abstract base class for session input buffers that stream data from * an arbitrary {@link InputStream}. This class buffers input data in * an internal byte array for optimal input performance. *

* {@link #readLine(CharArrayBuffer, InputStream)} method of this class treat a lone * LF as valid line delimiters in addition to CR-LF required * by the HTTP specification. * * @since 4.3 */ public class SessionInputBufferImpl implements SessionInputBuffer { private final BasicHttpTransportMetrics metrics; private final byte[] buffer; private final ByteArrayBuffer lineBuffer; private final int minChunkLimit; private final int maxLineLen; private final CharsetDecoder decoder; private int bufferPos; private int bufferLen; private CharBuffer cbuf; /** * Creates new instance of SessionInputBufferImpl. * * @param metrics HTTP transport metrics. * @param bufferSize buffer size. Must be a positive number. * @param minChunkLimit size limit below which data chunks should be buffered in memory * in order to minimize native method invocations on the underlying network socket. * The optimal value of this parameter can be platform specific and defines a trade-off * between performance of memory copy operations and that of native method invocation. * If negative default chunk limited will be used. * @param maxLineLen maximum line length. * @param charDecoder charDecoder to be used for decoding HTTP protocol elements. * If {@code null} simple type cast will be used for byte to char conversion. */ public SessionInputBufferImpl( final BasicHttpTransportMetrics metrics, final int bufferSize, final int minChunkLimit, final int maxLineLen, final CharsetDecoder charDecoder) { Args.notNull(metrics, "HTTP transport metrics"); Args.positive(bufferSize, "Buffer size"); this.metrics = metrics; this.buffer = new byte[bufferSize]; this.bufferPos = 0; this.bufferLen = 0; this.minChunkLimit = minChunkLimit >= 0 ? minChunkLimit : 512; this.maxLineLen = Math.max(maxLineLen, 0); this.lineBuffer = new ByteArrayBuffer(bufferSize); this.decoder = charDecoder; } public SessionInputBufferImpl( final BasicHttpTransportMetrics metrics, final int bufferSize) { this(metrics, bufferSize, bufferSize, 0, null); } public SessionInputBufferImpl(final int bufferSize, final int maxLineLen) { this(new BasicHttpTransportMetrics(), bufferSize, bufferSize, maxLineLen, null); } public SessionInputBufferImpl(final int bufferSize, final CharsetDecoder decoder) { this(new BasicHttpTransportMetrics(), bufferSize, bufferSize, 0, decoder); } public SessionInputBufferImpl(final int bufferSize) { this(new BasicHttpTransportMetrics(), bufferSize, bufferSize, 0, null); } @Override public int capacity() { return this.buffer.length; } @Override public int length() { return this.bufferLen - this.bufferPos; } @Override public int available() { return capacity() - length(); } public int fillBuffer(final InputStream inputStream) throws IOException { Args.notNull(inputStream, "Input stream"); // compact the buffer if necessary if (this.bufferPos > 0) { final int len = this.bufferLen - this.bufferPos; if (len > 0) { System.arraycopy(this.buffer, this.bufferPos, this.buffer, 0, len); } this.bufferPos = 0; this.bufferLen = len; } final int readLen; final int off = this.bufferLen; final int len = this.buffer.length - off; readLen = inputStream.read(this.buffer, off, len); if (readLen == -1) { return -1; } this.bufferLen = off + readLen; this.metrics.incrementBytesTransferred(readLen); return readLen; } public boolean hasBufferedData() { return this.bufferPos < this.bufferLen; } public void clear() { this.bufferPos = 0; this.bufferLen = 0; } @Override public int read(final InputStream inputStream) throws IOException { Args.notNull(inputStream, "Input stream"); int readLen; while (!hasBufferedData()) { readLen = fillBuffer(inputStream); if (readLen == -1) { return -1; } } return this.buffer[this.bufferPos++] & 0xff; } @Override public int read(final byte[] b, final int off, final int len, final InputStream inputStream) throws IOException { Args.notNull(inputStream, "Input stream"); if (b == null) { return 0; } if (hasBufferedData()) { final int chunk = Math.min(len, this.bufferLen - this.bufferPos); System.arraycopy(this.buffer, this.bufferPos, b, off, chunk); this.bufferPos += chunk; return chunk; } // If the remaining capacity is big enough, read directly from the // underlying input stream bypassing the buffer. if (len > this.minChunkLimit) { final int read = inputStream.read(b, off, len); if (read > 0) { this.metrics.incrementBytesTransferred(read); } return read; } // otherwise read to the buffer first while (!hasBufferedData()) { final int readLen = fillBuffer(inputStream); if (readLen == -1) { return -1; } } final int chunk = Math.min(len, this.bufferLen - this.bufferPos); System.arraycopy(this.buffer, this.bufferPos, b, off, chunk); this.bufferPos += chunk; return chunk; } @Override public int read(final byte[] b, final InputStream inputStream) throws IOException { if (b == null) { return 0; } return read(b, 0, b.length, inputStream); } /** * Reads a complete line of characters up to a line delimiter from this * session buffer into the given line buffer. The number of chars actually * read is returned as an integer. The line delimiter itself is discarded. * If no char is available because the end of the stream has been reached, * the value {@code -1} is returned. This method blocks until input * data is available, end of file is detected, or an exception is thrown. *

* This method treats a lone LF as a valid line delimiters in addition * to CR-LF required by the HTTP specification. * * @param charBuffer the line buffer, one line of characters upon return * @return the total number of bytes read into the buffer, or * {@code -1} is there is no more data because the end of * the stream has been reached. * @throws IOException if an I/O error occurs. */ @Override public int readLine(final CharArrayBuffer charBuffer, final InputStream inputStream) throws IOException { Args.notNull(charBuffer, "Char array buffer"); Args.notNull(inputStream, "Input stream"); int readLen = 0; boolean retry = true; while (retry) { // attempt to find end of line (LF) int pos = -1; for (int i = this.bufferPos; i < this.bufferLen; i++) { if (this.buffer[i] == Chars.LF) { pos = i; break; } } if (this.maxLineLen > 0) { final int currentLen = this.lineBuffer.length() + (pos >= 0 ? pos : this.bufferLen) - this.bufferPos; if (currentLen >= this.maxLineLen) { throw new MessageConstraintException("Maximum line length limit exceeded"); } } if (pos != -1) { // end of line found. if (this.lineBuffer.isEmpty()) { // the entire line is preset in the read buffer return lineFromReadBuffer(charBuffer, pos); } retry = false; final int len = pos + 1 - this.bufferPos; this.lineBuffer.append(this.buffer, this.bufferPos, len); this.bufferPos = pos + 1; } else { // end of line not found if (hasBufferedData()) { final int len = this.bufferLen - this.bufferPos; this.lineBuffer.append(this.buffer, this.bufferPos, len); this.bufferPos = this.bufferLen; } readLen = fillBuffer(inputStream); if (readLen == -1) { retry = false; } } } if (readLen == -1 && this.lineBuffer.isEmpty()) { // indicate the end of stream return -1; } return lineFromLineBuffer(charBuffer); } /** * Reads a complete line of characters up to a line delimiter from this * session buffer. The line delimiter itself is discarded. If no char is * available because the end of the stream has been reached, * {@code null} is returned. This method blocks until input data is * available, end of file is detected, or an exception is thrown. *

* This method treats a lone LF as a valid line delimiters in addition * to CR-LF required by the HTTP specification. * * @return HTTP line as a string * @throws IOException if an I/O error occurs. */ private int lineFromLineBuffer(final CharArrayBuffer charBuffer) throws IOException { // discard LF if found int len = this.lineBuffer.length(); if (len > 0) { if (this.lineBuffer.byteAt(len - 1) == Chars.LF) { len--; } // discard CR if found if (len > 0 && this.lineBuffer.byteAt(len - 1) == Chars.CR) { len--; } } if (this.decoder == null) { charBuffer.append(this.lineBuffer, 0, len); } else { final ByteBuffer bbuf = ByteBuffer.wrap(this.lineBuffer.array(), 0, len); len = appendDecoded(charBuffer, bbuf); } this.lineBuffer.clear(); return len; } private int lineFromReadBuffer(final CharArrayBuffer charbuffer, final int position) throws IOException { int pos = position; final int off = this.bufferPos; int len; this.bufferPos = pos + 1; if (pos > off && this.buffer[pos - 1] == Chars.CR) { // skip CR if found pos--; } len = pos - off; if (this.decoder == null) { charbuffer.append(this.buffer, off, len); } else { final ByteBuffer bbuf = ByteBuffer.wrap(this.buffer, off, len); len = appendDecoded(charbuffer, bbuf); } return len; } private int appendDecoded( final CharArrayBuffer charbuffer, final ByteBuffer bbuf) throws IOException { if (!bbuf.hasRemaining()) { return 0; } if (this.cbuf == null) { this.cbuf = CharBuffer.allocate(1024); } this.decoder.reset(); int len = 0; while (bbuf.hasRemaining()) { final CoderResult result = this.decoder.decode(bbuf, this.cbuf, true); len += handleDecodingResult(result, charbuffer); } final CoderResult result = this.decoder.flush(this.cbuf); len += handleDecodingResult(result, charbuffer); this.cbuf.clear(); return len; } private int handleDecodingResult( final CoderResult result, final CharArrayBuffer charBuffer) throws IOException { if (result.isError()) { result.throwException(); } this.cbuf.flip(); final int len = this.cbuf.remaining(); while (this.cbuf.hasRemaining()) { charBuffer.append(this.cbuf.get()); } this.cbuf.compact(); return len; } @Override public HttpTransportMetrics getMetrics() { return this.metrics; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultClassicHttpRequestFactory.java0100664 0000000 0000000 00000004234 14245617503 031432 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.net.URI; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.HttpRequestFactory; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; /** * Default factory for creating {@link ClassicHttpRequest} objects. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class DefaultClassicHttpRequestFactory implements HttpRequestFactory { public static final DefaultClassicHttpRequestFactory INSTANCE = new DefaultClassicHttpRequestFactory(); @Override public ClassicHttpRequest newHttpRequest(final String method, final URI uri) { return new BasicClassicHttpRequest(method, uri); } @Override public ClassicHttpRequest newHttpRequest(final String method, final String uri) { return new BasicClassicHttpRequest(method, uri); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultBHttpServerConnectionFactory.java0100664 0000000 0000000 00000016525 14245617503 032076 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.net.Socket; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.CharCodingSupport; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; /** * Default factory for {@link org.apache.hc.core5.http.io.HttpServerConnection}s. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultBHttpServerConnectionFactory implements HttpConnectionFactory { private final String scheme; private final Http1Config http1Config; private final CharCodingConfig charCodingConfig; private final ContentLengthStrategy incomingContentStrategy; private final ContentLengthStrategy outgoingContentStrategy; private final HttpMessageParserFactory requestParserFactory; private final HttpMessageWriterFactory responseWriterFactory; public DefaultBHttpServerConnectionFactory( final String scheme, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final HttpMessageParserFactory requestParserFactory, final HttpMessageWriterFactory responseWriterFactory) { super(); this.scheme = scheme; this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.incomingContentStrategy = incomingContentStrategy; this.outgoingContentStrategy = outgoingContentStrategy; this.requestParserFactory = requestParserFactory; this.responseWriterFactory = responseWriterFactory; } public DefaultBHttpServerConnectionFactory( final String scheme, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final HttpMessageParserFactory requestParserFactory, final HttpMessageWriterFactory responseWriterFactory) { this(scheme, http1Config, charCodingConfig, null, null, requestParserFactory, responseWriterFactory); } public DefaultBHttpServerConnectionFactory( final String scheme, final Http1Config http1Config, final CharCodingConfig charCodingConfig) { this(scheme, http1Config, charCodingConfig, null, null, null, null); } @Override public DefaultBHttpServerConnection createConnection(final Socket socket) throws IOException { final DefaultBHttpServerConnection conn = new DefaultBHttpServerConnection( this.scheme, this.http1Config, CharCodingSupport.createDecoder(this.charCodingConfig), CharCodingSupport.createEncoder(this.charCodingConfig), this.incomingContentStrategy, this.outgoingContentStrategy, this.requestParserFactory, this.responseWriterFactory); conn.bind(socket); return conn; } /** * Create a new {@link Builder}. * * @since 5.1 */ public static Builder builder() { return new Builder(); } /** * Builder for {@link DefaultBHttpServerConnectionFactory}. * * @since 5.1 */ public static final class Builder { private String scheme; private Http1Config http1Config; private CharCodingConfig charCodingConfig; private ContentLengthStrategy incomingContentLengthStrategy; private ContentLengthStrategy outgoingContentLengthStrategy; private HttpMessageParserFactory requestParserFactory; private HttpMessageWriterFactory responseWriterFactory; private Builder() {} public Builder scheme(final String scheme) { this.scheme = scheme; return this; } public Builder http1Config(final Http1Config http1Config) { this.http1Config = http1Config; return this; } public Builder charCodingConfig(final CharCodingConfig charCodingConfig) { this.charCodingConfig = charCodingConfig; return this; } public Builder incomingContentLengthStrategy(final ContentLengthStrategy incomingContentLengthStrategy) { this.incomingContentLengthStrategy = incomingContentLengthStrategy; return this; } public Builder outgoingContentLengthStrategy(final ContentLengthStrategy outgoingContentLengthStrategy) { this.outgoingContentLengthStrategy = outgoingContentLengthStrategy; return this; } public Builder requestParserFactory( final HttpMessageParserFactory requestParserFactory) { this.requestParserFactory = requestParserFactory; return this; } public Builder responseWriterFactory( final HttpMessageWriterFactory responseWriterFactory) { this.responseWriterFactory = responseWriterFactory; return this; } public DefaultBHttpServerConnectionFactory build() { return new DefaultBHttpServerConnectionFactory( scheme, http1Config, charCodingConfig, incomingContentLengthStrategy, outgoingContentLengthStrategy, requestParserFactory, responseWriterFactory); } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/ResponseOutOfOrderException.java0100664 0000000 0000000 00000003106 14403631147 030422 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; /** * Signals an early (out of order) response. */ class ResponseOutOfOrderException extends IOException { /** * Required for serialization support. * * @see java.io.Serializable */ private static final long serialVersionUID = 7802054516041674757L; public ResponseOutOfOrderException() { super(); } }httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpResponseParser.java0100664 0000000 0000000 00000006704 14403631147 030123 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.LineParser; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.util.CharArrayBuffer; /** * HTTP response parser that obtain its input from an instance * of {@link org.apache.hc.core5.http.io.SessionInputBuffer}. * * @since 4.2 */ public class DefaultHttpResponseParser extends AbstractMessageParser { private final HttpResponseFactory responseFactory; /** * Creates new instance of DefaultHttpResponseParser. * * @param lineParser the line parser. If {@code null} * {@link org.apache.hc.core5.http.message.LazyLineParser#INSTANCE} will be used * @param responseFactory the response factory. If {@code null} * {@link DefaultClassicHttpResponseFactory#INSTANCE} will be used. * @param http1Config the message http1Config. If {@code null} * {@link Http1Config#DEFAULT} will be used. * * @since 4.3 */ public DefaultHttpResponseParser( final LineParser lineParser, final HttpResponseFactory responseFactory, final Http1Config http1Config) { super(lineParser, http1Config); this.responseFactory = responseFactory != null ? responseFactory : DefaultClassicHttpResponseFactory.INSTANCE; } /** * @since 4.3 */ public DefaultHttpResponseParser(final Http1Config http1Config) { this(null, null, http1Config); } /** * @since 4.3 */ public DefaultHttpResponseParser() { this(Http1Config.DEFAULT); } @Override protected ClassicHttpResponse createMessage(final CharArrayBuffer buffer) throws IOException, HttpException { final StatusLine statusline = getLineParser().parseStatusLine(buffer); final ClassicHttpResponse response = this.responseFactory.newHttpResponse(statusline.getStatusCode(), statusline.getReasonPhrase()); response.setVersion(statusline.getProtocolVersion()); return response; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java0100664 0000000 0000000 00000034521 14403631147 027010 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.UnsupportedHttpVersionException; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.HttpResponseInformationCallback; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * {@code HttpRequestExecutor} is a client side HTTP protocol handler based * on the blocking (classic) I/O model. *

* {@code HttpRequestExecutor} relies on {@link HttpProcessor} to generate * mandatory protocol headers for all outgoing messages and apply common, * cross-cutting message transformations to all incoming and outgoing messages. * Application specific processing can be implemented outside * {@code HttpRequestExecutor} once the request has been executed and * a response has been received. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class HttpRequestExecutor { public static final Timeout DEFAULT_WAIT_FOR_CONTINUE = Timeout.ofSeconds(3); private final Timeout waitForContinue; private final ConnectionReuseStrategy connReuseStrategy; private final Http1StreamListener streamListener; /** * Creates new instance of HttpRequestExecutor. * * @since 4.3 */ public HttpRequestExecutor( final Timeout waitForContinue, final ConnectionReuseStrategy connReuseStrategy, final Http1StreamListener streamListener) { super(); this.waitForContinue = Args.positive(waitForContinue, "Wait for continue time"); this.connReuseStrategy = connReuseStrategy != null ? connReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE; this.streamListener = streamListener; } public HttpRequestExecutor(final ConnectionReuseStrategy connReuseStrategy) { this(DEFAULT_WAIT_FOR_CONTINUE, connReuseStrategy, null); } public HttpRequestExecutor() { this(DEFAULT_WAIT_FOR_CONTINUE, null, null); } /** * Sends the request and obtain a response. * * @param request the request to execute. * @param conn the connection over which to execute the request. * @param informationCallback callback to execute upon receipt of information status (1xx). * May be null. * @param context the context * @return the response to the request. * * @throws IOException in case of an I/O error. * @throws HttpException in case of HTTP protocol violation or a processing * problem. */ public ClassicHttpResponse execute( final ClassicHttpRequest request, final HttpClientConnection conn, final HttpResponseInformationCallback informationCallback, final HttpContext context) throws IOException, HttpException { Args.notNull(request, "HTTP request"); Args.notNull(conn, "Client connection"); Args.notNull(context, "HTTP context"); try { context.setAttribute(HttpCoreContext.SSL_SESSION, conn.getSSLSession()); context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, conn.getEndpointDetails()); conn.sendRequestHeader(request); if (streamListener != null) { streamListener.onRequestHead(conn, request); } boolean expectContinue = false; final HttpEntity entity = request.getEntity(); if (entity != null) { final Header expect = request.getFirstHeader(HttpHeaders.EXPECT); expectContinue = expect != null && HeaderElements.CONTINUE.equalsIgnoreCase(expect.getValue()); if (!expectContinue) { conn.sendRequestEntity(request); } } conn.flush(); ClassicHttpResponse response = null; while (response == null) { if (expectContinue) { if (conn.isDataAvailable(this.waitForContinue)) { response = conn.receiveResponseHeader(); if (streamListener != null) { streamListener.onResponseHead(conn, response); } final int status = response.getCode(); if (status == HttpStatus.SC_CONTINUE) { // discard 100-continue response = null; conn.sendRequestEntity(request); } else if (status < HttpStatus.SC_SUCCESS) { if (informationCallback != null) { informationCallback.execute(response, conn, context); } response = null; continue; } else if (status >= HttpStatus.SC_CLIENT_ERROR){ conn.terminateRequest(request); } else { conn.sendRequestEntity(request); } } else { conn.sendRequestEntity(request); } conn.flush(); expectContinue = false; } else { response = conn.receiveResponseHeader(); if (streamListener != null) { streamListener.onResponseHead(conn, response); } final int status = response.getCode(); if (status < HttpStatus.SC_INFORMATIONAL) { throw new ProtocolException("Invalid response: " + new StatusLine(response)); } if (status < HttpStatus.SC_SUCCESS) { if (informationCallback != null && status != HttpStatus.SC_CONTINUE) { informationCallback.execute(response, conn, context); } response = null; } } } if (MessageSupport.canResponseHaveBody(request.getMethod(), response)) { conn.receiveResponseEntity(response); } return response; } catch (final HttpException | IOException | RuntimeException ex) { Closer.closeQuietly(conn); throw ex; } } /** * Sends the request and obtain a response. * * @param request the request to execute. * @param conn the connection over which to execute the request. * @param context the context * @return the response to the request. * * @throws IOException in case of an I/O error. * @throws HttpException in case of HTTP protocol violation or a processing * problem. */ public ClassicHttpResponse execute( final ClassicHttpRequest request, final HttpClientConnection conn, final HttpContext context) throws IOException, HttpException { return execute(request, conn, null, context); } /** * Pre-process the given request using the given protocol processor and * initiates the process of request execution. * * @param request the request to prepare * @param processor the processor to use * @param context the context for sending the request * * @throws IOException in case of an I/O error. * @throws HttpException in case of HTTP protocol violation or a processing * problem. */ public void preProcess( final ClassicHttpRequest request, final HttpProcessor processor, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); Args.notNull(processor, "HTTP processor"); Args.notNull(context, "HTTP context"); final ProtocolVersion transportVersion = request.getVersion(); if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) { throw new UnsupportedHttpVersionException(transportVersion); } context.setProtocolVersion(transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); processor.process(request, request.getEntity(), context); } /** * Post-processes the given response using the given protocol processor and * completes the process of request execution. *

* This method does not read the response entity, if any. * The connection over which content of the response entity is being * streamed from cannot be reused until the response entity has been * fully consumed. * * @param response the response object to post-process * @param processor the processor to use * @param context the context for post-processing the response * * @throws IOException in case of an I/O error. * @throws HttpException in case of HTTP protocol violation or a processing * problem. */ public void postProcess( final ClassicHttpResponse response, final HttpProcessor processor, final HttpContext context) throws HttpException, IOException { Args.notNull(response, "HTTP response"); Args.notNull(processor, "HTTP processor"); Args.notNull(context, "HTTP context"); final ProtocolVersion transportVersion = response.getVersion(); context.setProtocolVersion(transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1); context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); processor.process(response, response.getEntity(), context); } /** * Determines whether the connection can be kept alive and is safe to be re-used for subsequent message exchanges. * * @param request current request object. * @param response current response object. * @param connection actual connection. * @param context current context. * @return {@code true} is the connection can be kept-alive and re-used. * @throws IOException in case of an I/O error. */ public boolean keepAlive( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpClientConnection connection, final HttpContext context) throws IOException { Args.notNull(connection, "HTTP connection"); Args.notNull(request, "HTTP request"); Args.notNull(response, "HTTP response"); Args.notNull(context, "HTTP context"); final boolean keepAlive = connection.isConsistent() && connReuseStrategy.keepAlive(request, response, context); if (streamListener != null) { streamListener.onExchangeComplete(connection, keepAlive); } return keepAlive; } /** * Create a new {@link Builder}. * * @since 5.2 */ public static Builder builder() { return new Builder(); } /** * Builder for {@link HttpRequestExecutor}. * * @since 5.2 */ public static final class Builder { private Timeout waitForContinue; private ConnectionReuseStrategy connReuseStrategy; private Http1StreamListener streamListener; private Builder() {} public Builder withWaitForContinue(final Timeout waitForContinue) { this.waitForContinue = waitForContinue; return this; } public Builder withConnectionReuseStrategy(final ConnectionReuseStrategy connReuseStrategy) { this.connReuseStrategy = connReuseStrategy; return this; } public Builder withHttp1StreamListener(final Http1StreamListener streamListener) { this.streamListener = streamListener; return this; } /** * Create a new HTTP Request Executor. * * @since 5.2 */ public HttpRequestExecutor build() { return new HttpRequestExecutor( waitForContinue, connReuseStrategy, streamListener); } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/BHttpConnectionBase.java0100664 0000000 0000000 00000033254 14435411677 026650 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.BasicEndpointDetails; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.io.BHttpConnection; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.apache.hc.core5.http.io.entity.EmptyInputStream; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.net.InetAddressUtils; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; class BHttpConnectionBase implements BHttpConnection { private static final Timeout STALE_CHECK_TIMEOUT = Timeout.ofMilliseconds(1); final Http1Config http1Config; final SessionInputBufferImpl inBuffer; final SessionOutputBufferImpl outbuffer; final BasicHttpConnectionMetrics connMetrics; final AtomicReference socketHolderRef; // Lazily initialized chunked request buffer provided to ChunkedOutputStream. private byte[] chunkedRequestBuffer; volatile ProtocolVersion version; volatile EndpointDetails endpointDetails; BHttpConnectionBase( final Http1Config http1Config, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder) { this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; final BasicHttpTransportMetrics inTransportMetrics = new BasicHttpTransportMetrics(); final BasicHttpTransportMetrics outTransportMetrics = new BasicHttpTransportMetrics(); this.inBuffer = new SessionInputBufferImpl(inTransportMetrics, this.http1Config.getBufferSize(), -1, this.http1Config.getMaxLineLength(), charDecoder); this.outbuffer = new SessionOutputBufferImpl(outTransportMetrics, this.http1Config.getBufferSize(), this.http1Config.getChunkSizeHint(), charEncoder); this.connMetrics = new BasicHttpConnectionMetrics(inTransportMetrics, outTransportMetrics); this.socketHolderRef = new AtomicReference<>(); } protected SocketHolder ensureOpen() throws IOException { final SocketHolder socketHolder = this.socketHolderRef.get(); if (socketHolder == null) { throw new ConnectionClosedException(); } return socketHolder; } /** * Binds this connection to the given {@link Socket}. This socket will be * used by the connection to send and receive data. *

* After this method's execution the connection status will be reported * as open and the {@link #isOpen()} will return {@code true}. * * @param socket the socket. * @throws IOException in case of an I/O error. */ protected void bind(final Socket socket) throws IOException { Args.notNull(socket, "Socket"); bind(new SocketHolder(socket)); } protected void bind(final SocketHolder socketHolder) throws IOException { Args.notNull(socketHolder, "Socket holder"); this.socketHolderRef.set(socketHolder); this.endpointDetails = null; } @Override public boolean isOpen() { return this.socketHolderRef.get() != null; } /** * @since 5.0 */ @Override public ProtocolVersion getProtocolVersion() { return this.version; } protected SocketHolder getSocketHolder() { return this.socketHolderRef.get(); } protected OutputStream createContentOutputStream( final long len, final SessionOutputBuffer buffer, final OutputStream outputStream, final Supplier> trailers) { if (len >= 0) { return new ContentLengthOutputStream(buffer, outputStream, len); } else if (len == ContentLengthStrategy.CHUNKED) { return new ChunkedOutputStream(buffer, outputStream, getChunkedRequestBuffer(), trailers); } else { return new IdentityOutputStream(buffer, outputStream); } } private byte[] getChunkedRequestBuffer() { if (chunkedRequestBuffer == null) { final int chunkSizeHint = this.http1Config.getChunkSizeHint(); chunkedRequestBuffer = new byte[chunkSizeHint > 0 ? chunkSizeHint : 8192]; } return chunkedRequestBuffer; } protected InputStream createContentInputStream( final long len, final SessionInputBuffer buffer, final InputStream inputStream) { if (len > 0) { return new ContentLengthInputStream(buffer, inputStream, len); } else if (len == 0) { return EmptyInputStream.INSTANCE; } else if (len == ContentLengthStrategy.CHUNKED) { return new ChunkedInputStream(buffer, inputStream, this.http1Config); } else { return new IdentityInputStream(buffer, inputStream); } } HttpEntity createIncomingEntity( final HttpMessage message, final SessionInputBuffer inBuffer, final InputStream inputStream, final long len) { return new IncomingHttpEntity( createContentInputStream(len, inBuffer, inputStream), len >= 0 ? len : -1, len == ContentLengthStrategy.CHUNKED, message.getFirstHeader(HttpHeaders.CONTENT_TYPE), message.getFirstHeader(HttpHeaders.CONTENT_ENCODING)); } @Override public SocketAddress getRemoteAddress() { final SocketHolder socketHolder = this.socketHolderRef.get(); return socketHolder != null ? socketHolder.getSocket().getRemoteSocketAddress() : null; } @Override public SocketAddress getLocalAddress() { final SocketHolder socketHolder = this.socketHolderRef.get(); return socketHolder != null ? socketHolder.getSocket().getLocalSocketAddress() : null; } @Override public void setSocketTimeout(final Timeout timeout) { final SocketHolder socketHolder = this.socketHolderRef.get(); if (socketHolder != null) { try { socketHolder.getSocket().setSoTimeout(Timeout.defaultsToDisabled(timeout).toMillisecondsIntBound()); } catch (final SocketException ignore) { // It is not quite clear from the Sun's documentation if there are any // other legitimate cases for a socket exception to be thrown when setting // SO_TIMEOUT besides the socket being already closed } } } @Override public Timeout getSocketTimeout() { final SocketHolder socketHolder = this.socketHolderRef.get(); if (socketHolder != null) { try { return Timeout.ofMilliseconds(socketHolder.getSocket().getSoTimeout()); } catch (final SocketException ignore) { } } return Timeout.DISABLED; } @Override public void close(final CloseMode closeMode) { final SocketHolder socketHolder = this.socketHolderRef.getAndSet(null); if (socketHolder != null) { final Socket socket = socketHolder.getSocket(); try { if (closeMode == CloseMode.IMMEDIATE) { // force abortive close (RST) socket.setSoLinger(true, 0); } } catch (final IOException ignore) { } finally { Closer.closeQuietly(socket); } } } @Override public void close() throws IOException { final SocketHolder socketHolder = this.socketHolderRef.getAndSet(null); if (socketHolder != null) { try (final Socket socket = socketHolder.getSocket()) { this.inBuffer.clear(); this.outbuffer.flush(socketHolder.getOutputStream()); } } } private int fillInputBuffer(final Timeout timeout) throws IOException { final SocketHolder socketHolder = ensureOpen(); final Socket socket = socketHolder.getSocket(); final int oldtimeout = socket.getSoTimeout(); try { socket.setSoTimeout(timeout.toMillisecondsIntBound()); return this.inBuffer.fillBuffer(socketHolder.getInputStream()); } finally { socket.setSoTimeout(oldtimeout); } } protected boolean awaitInput(final Timeout timeout) throws IOException { if (this.inBuffer.hasBufferedData()) { return true; } fillInputBuffer(timeout); return this.inBuffer.hasBufferedData(); } @Override public boolean isDataAvailable(final Timeout timeout) throws IOException { ensureOpen(); try { return awaitInput(timeout); } catch (final SocketTimeoutException ex) { return false; } } @Override public boolean isStale() throws IOException { if (!isOpen()) { return true; } try { final int bytesRead = fillInputBuffer(STALE_CHECK_TIMEOUT); return bytesRead < 0; } catch (final SocketTimeoutException ex) { return false; } catch (final SocketException ex) { return true; } } @Override public void flush() throws IOException { final SocketHolder socketHolder = ensureOpen(); this.outbuffer.flush(socketHolder.getOutputStream()); } protected void incrementRequestCount() { this.connMetrics.incrementRequestCount(); } protected void incrementResponseCount() { this.connMetrics.incrementResponseCount(); } @Override public SSLSession getSSLSession() { final SocketHolder socketHolder = this.socketHolderRef.get(); if (socketHolder != null) { final Socket socket = socketHolder.getSocket(); return socket instanceof SSLSocket ? ((SSLSocket) socket).getSession() : null; } return null; } @Override public EndpointDetails getEndpointDetails() { if (endpointDetails == null) { final SocketHolder socketHolder = this.socketHolderRef.get(); if (socketHolder != null) { @SuppressWarnings("resource") final Socket socket = socketHolder.getSocket(); Timeout socketTimeout; try { socketTimeout = Timeout.ofMilliseconds(socket.getSoTimeout()); } catch (final SocketException e) { socketTimeout = Timeout.DISABLED; } endpointDetails = new BasicEndpointDetails( socket.getRemoteSocketAddress(), socket.getLocalSocketAddress(), this.connMetrics, socketTimeout); } } return endpointDetails; } @Override public String toString() { final SocketHolder socketHolder = this.socketHolderRef.get(); if (socketHolder != null) { final Socket socket = socketHolder.getSocket(); final StringBuilder buffer = new StringBuilder(); final SocketAddress remoteAddress = socket.getRemoteSocketAddress(); final SocketAddress localAddress = socket.getLocalSocketAddress(); if (remoteAddress != null && localAddress != null) { InetAddressUtils.formatAddress(buffer, localAddress); buffer.append("<->"); InetAddressUtils.formatAddress(buffer, remoteAddress); } return buffer.toString(); } return "[Not bound]"; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/SocketHolder.java0100664 0000000 0000000 00000006067 14245617503 025377 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.util.Args; /** * Utility class that holds a {@link Socket} along with copies of its {@link InputStream} * and {@link OutputStream}. * * @since 5.0 */ public class SocketHolder { private final Socket socket; private final AtomicReference inputStreamRef; private final AtomicReference outputStreamRef; public SocketHolder(final Socket socket) { this.socket = Args.notNull(socket, "Socket"); this.inputStreamRef = new AtomicReference<>(); this.outputStreamRef = new AtomicReference<>(); } public final Socket getSocket() { return socket; } public final InputStream getInputStream() throws IOException { InputStream local = inputStreamRef.get(); if (local != null) { return local; } local = getInputStream(socket); if (inputStreamRef.compareAndSet(null, local)) { return local; } return inputStreamRef.get(); } protected InputStream getInputStream(final Socket socket) throws IOException { return socket.getInputStream(); } protected OutputStream getOutputStream(final Socket socket) throws IOException { return socket.getOutputStream(); } public final OutputStream getOutputStream() throws IOException { OutputStream local = outputStreamRef.get(); if (local != null) { return local; } local = getOutputStream(socket); if (outputStreamRef.compareAndSet(null, local)) { return local; } return outputStreamRef.get(); } @Override public String toString() { return socket.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpRequestWriterFactory.java0100664 0000000 0000000 00000004603 14245617503 031325 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.io.HttpMessageWriter; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; import org.apache.hc.core5.http.message.BasicLineFormatter; import org.apache.hc.core5.http.message.LineFormatter; /** * Default factory for request message writers. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultHttpRequestWriterFactory implements HttpMessageWriterFactory { public static final DefaultHttpRequestWriterFactory INSTANCE = new DefaultHttpRequestWriterFactory(); private final LineFormatter lineFormatter; public DefaultHttpRequestWriterFactory(final LineFormatter lineFormatter) { super(); this.lineFormatter = lineFormatter != null ? lineFormatter : BasicLineFormatter.INSTANCE; } public DefaultHttpRequestWriterFactory() { this(null); } @Override public HttpMessageWriter create() { return new DefaultHttpRequestWriter(this.lineFormatter); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/ChunkedInputStream.java0100664 0000000 0000000 00000026674 14403631147 026570 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.MalformedChunkCodingException; import org.apache.hc.core5.http.StreamClosedException; import org.apache.hc.core5.http.TruncatedChunkException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Implements chunked transfer coding. The content is received in small chunks. * Entities transferred using this input stream can be of unlimited length. * After the stream is read to the end, it provides access to the trailers, * if any. *

* Note that this class NEVER closes the underlying stream, even when * {@link #close()} gets called. Instead, it will read until the "end" of its * chunking on close, which allows for the seamless execution of subsequent * HTTP 1.1 requests, while not requiring the client to remember to read the * entire contents of the response. * * * @since 4.0 * */ public class ChunkedInputStream extends InputStream { private enum State { CHUNK_LEN, CHUNK_DATA, CHUNK_CRLF, CHUNK_INVALID } private static final int BUFFER_SIZE = 2048; private static final Header[] EMPTY_FOOTERS = {}; /** The session input buffer */ private final SessionInputBuffer buffer; private final InputStream inputStream; private final CharArrayBuffer lineBuffer; private final Http1Config http1Config; private State state; /** The chunk size */ private long chunkSize; /** The current position within the current chunk */ private long pos; /** True if we've reached the end of stream */ private boolean eof; /** True if this stream is closed */ private boolean closed; private Header[] footers = EMPTY_FOOTERS; /** * Default constructor. * * @param buffer Session input buffer * @param inputStream Input stream * @param http1Config Message http1Config. If {@code null} {@link Http1Config#DEFAULT} will be used. * * @since 4.4 */ public ChunkedInputStream(final SessionInputBuffer buffer, final InputStream inputStream, final Http1Config http1Config) { super(); this.buffer = Args.notNull(buffer, "Session input buffer"); this.inputStream = Args.notNull(inputStream, "Input stream"); this.pos = 0L; this.lineBuffer = new CharArrayBuffer(16); this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.state = State.CHUNK_LEN; } /** * Wraps session input stream and reads chunk coded input. * * @param buffer Session input buffer * @param inputStream Input stream */ public ChunkedInputStream(final SessionInputBuffer buffer, final InputStream inputStream) { this(buffer, inputStream, null); } @Override public int available() throws IOException { final int len = this.buffer.length(); return (int) Math.min(len, this.chunkSize - this.pos); } /** *

Returns all the data in a chunked stream in coalesced form. A chunk * is followed by a CRLF. The method returns -1 as soon as a chunksize of 0 * is detected.

* *

Trailer headers are read automatically at the end of the stream and * can be obtained with the getResponseFooters() method.

* * @return -1 of the end of the stream has been reached or the next data * byte * @throws IOException in case of an I/O error */ @Override public int read() throws IOException { if (this.closed) { throw new StreamClosedException(); } if (this.eof) { return -1; } if (state != State.CHUNK_DATA) { nextChunk(); if (this.eof) { return -1; } } final int b = buffer.read(inputStream); if (b != -1) { pos++; if (pos >= chunkSize) { state = State.CHUNK_CRLF; } } return b; } /** * Read some bytes from the stream. * @param b The byte array that will hold the contents from the stream. * @param off The offset into the byte array at which bytes will start to be * placed. * @param len the maximum number of bytes that can be returned. * @return The number of bytes returned or -1 if the end of stream has been * reached. * @throws IOException in case of an I/O error */ @Override public int read (final byte[] b, final int off, final int len) throws IOException { if (closed) { throw new StreamClosedException(); } if (eof) { return -1; } if (state != State.CHUNK_DATA) { nextChunk(); if (eof) { return -1; } } final int bytesRead = buffer.read(b, off, (int) Math.min(len, chunkSize - pos), inputStream); if (bytesRead != -1) { pos += bytesRead; if (pos >= chunkSize) { state = State.CHUNK_CRLF; } return bytesRead; } eof = true; throw new TruncatedChunkException("Truncated chunk (expected size: %d; actual size: %d)", chunkSize, pos); } /** * Read some bytes from the stream. * @param b The byte array that will hold the contents from the stream. * @return The number of bytes returned or -1 if the end of stream has been * reached. * @throws IOException in case of an I/O error */ @Override public int read (final byte[] b) throws IOException { return read(b, 0, b.length); } /** * Read the next chunk. * @throws IOException in case of an I/O error */ private void nextChunk() throws IOException { if (state == State.CHUNK_INVALID) { throw new MalformedChunkCodingException("Corrupt data stream"); } try { chunkSize = getChunkSize(); if (chunkSize < 0L) { throw new MalformedChunkCodingException("Negative chunk size"); } state = State.CHUNK_DATA; pos = 0L; if (chunkSize == 0L) { eof = true; parseTrailerHeaders(); } } catch (final MalformedChunkCodingException ex) { state = State.CHUNK_INVALID; throw ex; } } /** * Expects the stream to start with a chunksize in hex with optional * comments after a semicolon. The line must end with a CRLF: "a3; some * comment\r\n" Positions the stream at the start of the next line. */ private long getChunkSize() throws IOException { final State st = this.state; switch (st) { case CHUNK_CRLF: lineBuffer.clear(); final int bytesRead1 = this.buffer.readLine(lineBuffer, inputStream); if (bytesRead1 == -1) { throw new MalformedChunkCodingException( "CRLF expected at end of chunk"); } if (!lineBuffer.isEmpty()) { throw new MalformedChunkCodingException( "Unexpected content at the end of chunk"); } state = State.CHUNK_LEN; //$FALL-THROUGH$ case CHUNK_LEN: lineBuffer.clear(); final int bytesRead2 = this.buffer.readLine(lineBuffer, inputStream); if (bytesRead2 == -1) { throw new ConnectionClosedException( "Premature end of chunk coded message body: closing chunk expected"); } int separator = lineBuffer.indexOf(';'); if (separator < 0) { separator = lineBuffer.length(); } final String s = this.lineBuffer.substringTrimmed(0, separator); try { return Long.parseLong(s, 16); } catch (final NumberFormatException e) { throw new MalformedChunkCodingException("Bad chunk header: " + s); } default: throw new IllegalStateException("Inconsistent codec state"); } } /** * Reads and stores the Trailer headers. * @throws IOException in case of an I/O error */ private void parseTrailerHeaders() throws IOException { try { this.footers = AbstractMessageParser.parseHeaders(buffer, inputStream, http1Config.getMaxHeaderCount(), http1Config.getMaxLineLength(), null); } catch (final HttpException ex) { final IOException ioe = new MalformedChunkCodingException("Invalid trailing header: " + ex.getMessage()); ioe.initCause(ex); throw ioe; } } /** * Reads the remainder of the chunked message, leaving the underlying * stream at a position to start reading the next response without * scanning. But does NOT close the underlying stream. * @throws IOException in case of an I/O error */ @Override public void close() throws IOException { if (!closed) { try { if (!eof && state != State.CHUNK_INVALID) { // Optimistically check if the content has been fully read // when there's no data remaining in the current chunk. // This is common when self-terminating content (e.g. JSON) // is parsed from response streams. if (chunkSize == pos && chunkSize > 0 && read() == -1) { return; } // read and discard the remainder of the message final byte[] buff = new byte[BUFFER_SIZE]; while (read(buff) >= 0) { } } } finally { eof = true; closed = true; } } } public Header[] getFooters() { return footers.length > 0 ? footers.clone() : EMPTY_FOOTERS; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/IdentityInputStream.java0100664 0000000 0000000 00000006500 14245617503 026766 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.http.StreamClosedException; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.util.Args; /** * Input stream that reads data without any transformation. The end of the * content entity is demarcated by closing the underlying connection * (EOF condition). Entities transferred using this input stream can be of * unlimited length. *

* Note that this class NEVER closes the underlying stream, even when * {@link #close()} gets called. Instead, the stream will be marked as * closed and no further reading will be permitted. * * @since 4.0 */ public class IdentityInputStream extends InputStream { private final SessionInputBuffer buffer; private final InputStream inputStream; private boolean closed; /** * Default constructor. * * @param buffer Session input buffer * @param inputStream Input stream */ public IdentityInputStream(final SessionInputBuffer buffer, final InputStream inputStream) { super(); this.buffer = Args.notNull(buffer, "Session input buffer"); this.inputStream = Args.notNull(inputStream, "Input stream"); } @Override public int available() throws IOException { if (this.closed) { return 0; } final int n = this.buffer.length(); return n > 0 ? n : this.inputStream.available(); } /** * Marks this stream as closed, but does NOT close the underlying stream. * @throws IOException If an I/O problem occurs. */ @Override public void close() throws IOException { this.closed = true; } @Override public int read() throws IOException { if (this.closed) { throw new StreamClosedException(); } return this.buffer.read(this.inputStream); } @Override public int read(final byte[] b, final int off, final int len) throws IOException { if (this.closed) { throw new StreamClosedException(); } return this.buffer.read(b, off, len, this.inputStream); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/AbstractMessageWriter.java0100664 0000000 0000000 00000010112 14245617503 027240 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import org.apache.hc.core5.http.FormattedHeader; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.io.HttpMessageWriter; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.apache.hc.core5.http.message.BasicLineFormatter; import org.apache.hc.core5.http.message.LineFormatter; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Abstract base class for HTTP message writers that serialize output to * an instance of {@link org.apache.hc.core5.http.io.SessionOutputBuffer}. * * @since 4.0 */ public abstract class AbstractMessageWriter implements HttpMessageWriter { private final CharArrayBuffer lineBuf; private final LineFormatter lineFormatter; /** * Creates an instance of AbstractMessageWriter. * * @param formatter the line formatter If {@code null} {@link BasicLineFormatter#INSTANCE} * will be used. * * @since 4.3 */ public AbstractMessageWriter(final LineFormatter formatter) { super(); this.lineFormatter = formatter != null ? formatter : BasicLineFormatter.INSTANCE; this.lineBuf = new CharArrayBuffer(128); } LineFormatter getLineFormatter() { return this.lineFormatter; } /** * Subclasses must override this method to write out the first header line * based on the {@link HttpMessage} passed as a parameter. * * @param message the message whose first line is to be written out. * @param lineBuf line buffer * @throws IOException in case of an I/O error. */ protected abstract void writeHeadLine(T message, CharArrayBuffer lineBuf) throws IOException; @Override public void write(final T message, final SessionOutputBuffer buffer, final OutputStream outputStream) throws IOException, HttpException { Args.notNull(message, "HTTP message"); Args.notNull(buffer, "Session output buffer"); Args.notNull(outputStream, "Output stream"); writeHeadLine(message, this.lineBuf); buffer.writeLine(this.lineBuf, outputStream); for (final Iterator

it = message.headerIterator(); it.hasNext(); ) { final Header header = it.next(); if (header instanceof FormattedHeader) { final CharArrayBuffer chbuffer = ((FormattedHeader) header).getBuffer(); buffer.writeLine(chbuffer, outputStream); } else { this.lineBuf.clear(); lineFormatter.formatHeader(this.lineBuf, header); buffer.writeLine(this.lineBuf, outputStream); } } this.lineBuf.clear(); buffer.writeLine(this.lineBuf, outputStream); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultBHttpServerConnection.java0100664 0000000 0000000 00000020235 14403631147 030533 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.UnsupportedHttpVersionException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.io.HttpMessageParser; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriter; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; import org.apache.hc.core5.http.io.HttpServerConnection; import org.apache.hc.core5.util.Args; /** * Default implementation of {@link HttpServerConnection}. * * @since 4.3 */ public class DefaultBHttpServerConnection extends BHttpConnectionBase implements HttpServerConnection { private final String scheme; private final ContentLengthStrategy incomingContentStrategy; private final ContentLengthStrategy outgoingContentStrategy; private final HttpMessageParser requestParser; private final HttpMessageWriter responseWriter; /** * Creates new instance of DefaultBHttpServerConnection. * * @param scheme protocol scheme * @param http1Config Message http1Config. If {@code null} * {@link Http1Config#DEFAULT} will be used. * @param charDecoder decoder to be used for decoding HTTP protocol elements. * If {@code null} simple type cast will be used for byte to char conversion. * @param charEncoder encoder to be used for encoding HTTP protocol elements. * If {@code null} simple type cast will be used for char to byte conversion. * @param incomingContentStrategy incoming content length strategy. If {@code null} * {@link DefaultContentLengthStrategy#INSTANCE} will be used. * @param outgoingContentStrategy outgoing content length strategy. If {@code null} * {@link DefaultContentLengthStrategy#INSTANCE} will be used. * @param requestParserFactory request parser factory. If {@code null} * {@link DefaultHttpRequestParserFactory#INSTANCE} will be used. * @param responseWriterFactory response writer factory. If {@code null} * {@link DefaultHttpResponseWriterFactory#INSTANCE} will be used. */ public DefaultBHttpServerConnection( final String scheme, final Http1Config http1Config, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final HttpMessageParserFactory requestParserFactory, final HttpMessageWriterFactory responseWriterFactory) { super(http1Config, charDecoder, charEncoder); this.scheme = scheme; this.requestParser = (requestParserFactory != null ? requestParserFactory : DefaultHttpRequestParserFactory.INSTANCE).create(http1Config); this.responseWriter = (responseWriterFactory != null ? responseWriterFactory : DefaultHttpResponseWriterFactory.INSTANCE).create(); this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy : DefaultContentLengthStrategy.INSTANCE; this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy : DefaultContentLengthStrategy.INSTANCE; } public DefaultBHttpServerConnection( final String scheme, final Http1Config http1Config, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder) { this(scheme, http1Config, charDecoder, charEncoder, null, null, null, null); } public DefaultBHttpServerConnection( final String scheme, final Http1Config http1Config) { this(scheme, http1Config, null, null); } protected void onRequestReceived(final ClassicHttpRequest request) { } protected void onResponseSubmitted(final ClassicHttpResponse response) { } @Override public void bind(final Socket socket) throws IOException { super.bind(socket); } @Override public ClassicHttpRequest receiveRequestHeader() throws HttpException, IOException { final SocketHolder socketHolder = ensureOpen(); final ClassicHttpRequest request = this.requestParser.parse(this.inBuffer, socketHolder.getInputStream()); if (request == null) { return null; } final ProtocolVersion transportVersion = request.getVersion(); if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) { throw new UnsupportedHttpVersionException(transportVersion); } request.setScheme(this.scheme); this.version = transportVersion; onRequestReceived(request); incrementRequestCount(); return request; } @Override public void receiveRequestEntity(final ClassicHttpRequest request) throws HttpException, IOException { Args.notNull(request, "HTTP request"); final SocketHolder socketHolder = ensureOpen(); final long len = this.incomingContentStrategy.determineLength(request); if (len == ContentLengthStrategy.UNDEFINED) { return; } request.setEntity(createIncomingEntity(request, this.inBuffer, socketHolder.getInputStream(), len)); } @Override public void sendResponseHeader(final ClassicHttpResponse response) throws HttpException, IOException { Args.notNull(response, "HTTP response"); final SocketHolder socketHolder = ensureOpen(); this.responseWriter.write(response, this.outbuffer, socketHolder.getOutputStream()); onResponseSubmitted(response); if (response.getCode() >= HttpStatus.SC_SUCCESS) { incrementResponseCount(); } } @Override public void sendResponseEntity(final ClassicHttpResponse response) throws HttpException, IOException { Args.notNull(response, "HTTP response"); final SocketHolder socketHolder = ensureOpen(); final HttpEntity entity = response.getEntity(); if (entity == null) { return; } final long len = this.outgoingContentStrategy.determineLength(response); try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), entity.getTrailers())) { entity.writeTo(outStream); } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpResponseWriter.java0100664 0000000 0000000 00000005167 14245617503 030151 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.LineFormatter; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.util.CharArrayBuffer; /** * HTTP response writer that serializes its output to an instance of * {@link org.apache.hc.core5.http.io.SessionOutputBuffer}. * * @since 4.3 */ public class DefaultHttpResponseWriter extends AbstractMessageWriter { /** * Creates an instance of DefaultHttpResponseWriter. * * @param formatter the line formatter If {@code null} * {@link org.apache.hc.core5.http.message.BasicLineFormatter#INSTANCE} * will be used. */ public DefaultHttpResponseWriter(final LineFormatter formatter) { super(formatter); } public DefaultHttpResponseWriter() { super(null); } @Override protected void writeHeadLine( final ClassicHttpResponse message, final CharArrayBuffer lineBuf) throws IOException { final ProtocolVersion transportVersion = message.getVersion(); getLineFormatter().formatStatusLine(lineBuf, new StatusLine( transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1, message.getCode(), message.getReasonPhrase())); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpResponseParserFactory.java0100664 0000000 0000000 00000005456 14245617503 031462 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.io.HttpMessageParser; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.message.LazyLaxLineParser; import org.apache.hc.core5.http.message.LineParser; /** * Default factory for response message parsers. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultHttpResponseParserFactory implements HttpMessageParserFactory { public static final DefaultHttpResponseParserFactory INSTANCE = new DefaultHttpResponseParserFactory(); private final LineParser lineParser; private final HttpResponseFactory responseFactory; public DefaultHttpResponseParserFactory(final LineParser lineParser, final HttpResponseFactory responseFactory) { super(); this.lineParser = lineParser != null ? lineParser : LazyLaxLineParser.INSTANCE; this.responseFactory = responseFactory != null ? responseFactory : DefaultClassicHttpResponseFactory.INSTANCE; } public DefaultHttpResponseParserFactory() { this(null, null); } @Override public HttpMessageParser create(final Http1Config http1Config) { return new DefaultHttpResponseParser(this.lineParser, this.responseFactory, http1Config); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/AbstractMessageParser.java0100664 0000000 0000000 00000026450 14403631147 027230 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.io.HttpMessageParser; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.http.message.LazyLineParser; import org.apache.hc.core5.http.message.LineParser; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; /** * Abstract base class for HTTP message parsers that obtain input from * an instance of {@link org.apache.hc.core5.http.io.SessionInputBuffer}. * * @since 4.0 */ public abstract class AbstractMessageParser implements HttpMessageParser { private static final int HEAD_LINE = 0; private static final int HEADERS = 1; private final Http1Config http1Config; private final List headerLines; private final CharArrayBuffer headLine; private final LineParser lineParser; private int state; private T message; /** * Creates new instance of AbstractMessageParser. * * @param lineParser the line parser. If {@code null} * {@link org.apache.hc.core5.http.message.LazyLineParser#INSTANCE} will be used. * @param http1Config the message http1Config. If {@code null} * {@link Http1Config#DEFAULT} will be used. * * @since 4.3 */ public AbstractMessageParser(final LineParser lineParser, final Http1Config http1Config) { super(); this.lineParser = lineParser != null ? lineParser : LazyLineParser.INSTANCE; this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.headerLines = new ArrayList<>(); this.headLine = new CharArrayBuffer(128); this.state = HEAD_LINE; } LineParser getLineParser() { return this.lineParser; } /** * Parses HTTP headers from the data receiver stream according to the generic * format as specified by the HTTP/1.1 protocol specification. * * @param inBuffer Session input buffer * @param inputStream Input stream * @param maxHeaderCount maximum number of headers allowed. If the number * of headers received from the data stream exceeds maxCount value, an * IOException will be thrown. Setting this parameter to a negative value * or zero will disable the check. * @param maxLineLen maximum number of characters for a header line, * including the continuation lines. Setting this parameter to a negative * value or zero will disable the check. * @return array of HTTP headers * @param lineParser the line parser. If {@code null} * {@link org.apache.hc.core5.http.message.LazyLineParser#INSTANCE} will be used * * @throws IOException in case of an I/O error * @throws HttpException in case of HTTP protocol violation */ public static Header[] parseHeaders( final SessionInputBuffer inBuffer, final InputStream inputStream, final int maxHeaderCount, final int maxLineLen, final LineParser lineParser) throws HttpException, IOException { final List headerLines = new ArrayList<>(); return parseHeaders(inBuffer, inputStream, maxHeaderCount, maxLineLen, lineParser != null ? lineParser : LazyLineParser.INSTANCE, headerLines); } /** * Parses HTTP headers from the data receiver stream according to the generic * format as specified by the HTTP/1.1 protocol specification. * * @param inBuffer Session input buffer * @param inputStream Input stream * @param maxHeaderCount maximum number of headers allowed. If the number * of headers received from the data stream exceeds maxCount value, an * IOException will be thrown. Setting this parameter to a negative value * or zero will disable the check. * @param maxLineLen maximum number of characters for a header line, * including the continuation lines. Setting this parameter to a negative * value or zero will disable the check. * @param parser line parser to use. * @param headerLines List of header lines. This list will be used to store * intermediate results. This makes it possible to resume parsing of * headers in case of a {@link java.io.InterruptedIOException}. * * @return array of HTTP headers * * @throws IOException in case of an I/O error * @throws HttpException in case of HTTP protocol violation * * @since 4.1 */ public static Header[] parseHeaders( final SessionInputBuffer inBuffer, final InputStream inputStream, final int maxHeaderCount, final int maxLineLen, final LineParser parser, final List headerLines) throws HttpException, IOException { Args.notNull(inBuffer, "Session input buffer"); Args.notNull(inputStream, "Input stream"); Args.notNull(parser, "Line parser"); Args.notNull(headerLines, "Header line list"); CharArrayBuffer current = null; CharArrayBuffer previous = null; for (;;) { if (current == null) { current = new CharArrayBuffer(64); } else { current.clear(); } final int readLen = inBuffer.readLine(current, inputStream); if (readLen == -1 || current.length() < 1) { break; } // Parse the header name and value // Check for folded headers first // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2 // discussion on folded headers if ((current.charAt(0) == ' ' || current.charAt(0) == '\t') && previous != null) { // we have continuation folded header // so append value int i = 0; while (i < current.length()) { final char ch = current.charAt(i); if (ch != ' ' && ch != '\t') { break; } i++; } if (maxLineLen > 0 && previous.length() + 1 + current.length() - i > maxLineLen) { throw new MessageConstraintException("Maximum line length limit exceeded"); } previous.append(' '); previous.append(current, i, current.length() - i); } else { headerLines.add(current); previous = current; current = null; } if (maxHeaderCount > 0 && headerLines.size() >= maxHeaderCount) { throw new MessageConstraintException("Maximum header count exceeded"); } } final Header[] headers = new Header[headerLines.size()]; for (int i = 0; i < headerLines.size(); i++) { final CharArrayBuffer buffer = headerLines.get(i); headers[i] = parser.parseHeader(buffer); } return headers; } /** * Subclasses must override this method to generate an instance of * {@link HttpMessage} based on the initial input from the session buffer. *

* Usually this method is expected to read just the very first line or * the very first valid from the data stream and based on the input generate * an appropriate instance of {@link HttpMessage}. * * @param buffer the session input buffer. * @return HTTP message based on the input from the session buffer. * @throws IOException in case of an I/O error. * @throws HttpException in case of HTTP protocol violation. * * @since 5.0 */ protected abstract T createMessage(CharArrayBuffer buffer) throws IOException, HttpException; /** * Subclasses must override this method to generate an appropriate exception * in case of unexpected connection termination by the peer endpoint. * * @since 5.0 * * @deprecated do not use. */ @Deprecated protected IOException createConnectionClosedException() { return new ConnectionClosedException(); } @Override public T parse(final SessionInputBuffer buffer, final InputStream inputStream) throws IOException, HttpException { Args.notNull(buffer, "Session input buffer"); Args.notNull(inputStream, "Input stream"); final int st = this.state; switch (st) { case HEAD_LINE: for (int n = 0; n < this.http1Config.getMaxEmptyLineCount(); n++) { this.headLine.clear(); final int i = buffer.readLine(this.headLine, inputStream); if (i == -1) { return null; } if (this.headLine.length() > 0) { this.message = createMessage(this.headLine); if (this.message != null) { break; } } } if (this.message == null) { throw new MessageConstraintException("Maximum empty line limit exceeded"); } this.state = HEADERS; //$FALL-THROUGH$ case HEADERS: final Header[] headers = AbstractMessageParser.parseHeaders( buffer, inputStream, this.http1Config.getMaxHeaderCount(), this.http1Config.getMaxLineLength(), this.lineParser, this.headerLines); this.message.setHeaders(headers); final T result = this.message; this.message = null; this.headerLines.clear(); this.state = HEAD_LINE; return result; default: throw new IllegalStateException("Inconsistent parser state"); } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/IdentityOutputStream.java0100664 0000000 0000000 00000006750 14245617503 027176 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.OutputStream; import org.apache.hc.core5.http.StreamClosedException; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.apache.hc.core5.util.Args; /** * Output stream that writes data without any transformation. The end of * the content entity is demarcated by closing the underlying connection * (EOF condition). Entities transferred using this input stream can be of * unlimited length. *

* Note that this class NEVER closes the underlying stream, even when * {@link #close()} gets called. Instead, the stream will be marked as * closed and no further output will be permitted. * * @since 4.0 */ public class IdentityOutputStream extends OutputStream { private final SessionOutputBuffer buffer; private final OutputStream outputStream; /** True if the stream is closed. */ private boolean closed; /** * Default constructor. * * @param buffer Session output buffer * @param outputStream Output stream */ public IdentityOutputStream(final SessionOutputBuffer buffer, final OutputStream outputStream) { super(); this.buffer = Args.notNull(buffer, "Session output buffer"); this.outputStream = Args.notNull(outputStream, "Output stream"); } /** * Finishes writing to the underlying stream, but does NOT close the underlying stream. * @throws IOException If an I/O problem occurs. */ @Override public void close() throws IOException { if (!this.closed) { this.closed = true; this.buffer.flush(this.outputStream); } } @Override public void flush() throws IOException { this.buffer.flush(this.outputStream); } @Override public void write(final byte[] b, final int off, final int len) throws IOException { if (this.closed) { throw new StreamClosedException(); } this.buffer.write(b, off, len, this.outputStream); } @Override public void write(final byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(final int b) throws IOException { if (this.closed) { throw new StreamClosedException(); } this.buffer.write(b, this.outputStream); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpRequestParser.java0100664 0000000 0000000 00000007770 14403631147 027761 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequestFactory; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.RequestHeaderFieldsTooLargeException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.http.message.LineParser; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.util.CharArrayBuffer; /** * HTTP request parser that obtain its input from an instance * of {@link org.apache.hc.core5.http.io.SessionInputBuffer}. * * @since 4.2 */ public class DefaultHttpRequestParser extends AbstractMessageParser { private final HttpRequestFactory requestFactory; /** * Creates new instance of DefaultHttpRequestParser. * * @param lineParser the line parser. If {@code null} * {@link org.apache.hc.core5.http.message.LazyLineParser#INSTANCE} will be used. * @param requestFactory the response factory. If {@code null} * {@link DefaultClassicHttpRequestFactory#INSTANCE} will be used. * @param http1Config the message http1Config. If {@code null} * {@link Http1Config#DEFAULT} will be used. * * @since 4.3 */ public DefaultHttpRequestParser( final LineParser lineParser, final HttpRequestFactory requestFactory, final Http1Config http1Config) { super(lineParser, http1Config); this.requestFactory = requestFactory != null ? requestFactory : DefaultClassicHttpRequestFactory.INSTANCE; } /** * @since 4.3 */ public DefaultHttpRequestParser(final Http1Config http1Config) { this(null, null, http1Config); } /** * @since 4.3 */ public DefaultHttpRequestParser() { this(Http1Config.DEFAULT); } @Override public ClassicHttpRequest parse( final SessionInputBuffer buffer, final InputStream inputStream) throws IOException, HttpException { try { return super.parse(buffer, inputStream); } catch (final MessageConstraintException ex) { throw new RequestHeaderFieldsTooLargeException(ex.getMessage(), ex); } } @Override protected ClassicHttpRequest createMessage(final CharArrayBuffer buffer) throws IOException, HttpException { final RequestLine requestLine = getLineParser().parseRequestLine(buffer); final ClassicHttpRequest request = this.requestFactory.newHttpRequest(requestLine.getMethod(), requestLine.getUri()); request.setVersion(requestLine.getProtocolVersion()); return request; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpRequestParserFactory.java0100664 0000000 0000000 00000005422 14245617503 031305 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.HttpRequestFactory; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.io.HttpMessageParser; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.message.LazyLineParser; import org.apache.hc.core5.http.message.LineParser; /** * Default factory for request message parsers. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) public class DefaultHttpRequestParserFactory implements HttpMessageParserFactory { public static final DefaultHttpRequestParserFactory INSTANCE = new DefaultHttpRequestParserFactory(); private final LineParser lineParser; private final HttpRequestFactory requestFactory; public DefaultHttpRequestParserFactory(final LineParser lineParser, final HttpRequestFactory requestFactory) { super(); this.lineParser = lineParser != null ? lineParser : LazyLineParser.INSTANCE; this.requestFactory = requestFactory != null ? requestFactory : DefaultClassicHttpRequestFactory.INSTANCE; } public DefaultHttpRequestParserFactory() { this(null, null); } @Override public HttpMessageParser create(final Http1Config http1Config) { return new DefaultHttpRequestParser(this.lineParser, this.requestFactory, http1Config); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpRequestWriter.java0100664 0000000 0000000 00000005162 14245617503 027776 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.LineFormatter; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.util.CharArrayBuffer; /** * HTTP request writer that serializes its output to an instance of * {@link org.apache.hc.core5.http.io.SessionOutputBuffer}. * * @since 4.3 */ public class DefaultHttpRequestWriter extends AbstractMessageWriter { /** * Creates an instance of DefaultHttpRequestWriter. * * @param formatter the line formatter If {@code null} * {@link org.apache.hc.core5.http.message.BasicLineFormatter#INSTANCE} * will be used. */ public DefaultHttpRequestWriter(final LineFormatter formatter) { super(formatter); } public DefaultHttpRequestWriter() { this(null); } @Override protected void writeHeadLine( final ClassicHttpRequest message, final CharArrayBuffer lineBuf) throws IOException { final ProtocolVersion transportVersion = message.getVersion(); getLineFormatter().formatRequestLine(lineBuf, new RequestLine( message.getMethod(), message.getRequestUri(), transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1)); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/MonitoringResponseOutOfOrderStrategy.java0100664 0000000 0000000 00000011207 14245617503 032341 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; import java.io.IOException; import java.io.InputStream; /** * A {@link ResponseOutOfOrderStrategy} implementation which checks for premature responses every {@link #chunkSize} * bytes. An 8 KiB chunk size is used by default based on testing using values between 4 KiB and 128 KiB. This is * optimized for correctness and results in a maximum upload speed of 8 MiB/s until {@link #maxChunksToCheck} is * reached. * * @since 5.1 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class MonitoringResponseOutOfOrderStrategy implements ResponseOutOfOrderStrategy { private static final int DEFAULT_CHUNK_SIZE = 8 * 1024; public static final MonitoringResponseOutOfOrderStrategy INSTANCE = new MonitoringResponseOutOfOrderStrategy(); private final long chunkSize; private final long maxChunksToCheck; /** * Instantiates a default {@link MonitoringResponseOutOfOrderStrategy}. {@link #INSTANCE} may be used instead. */ public MonitoringResponseOutOfOrderStrategy() { this(DEFAULT_CHUNK_SIZE); } /** * Instantiates a {@link MonitoringResponseOutOfOrderStrategy} with unlimited {@link #maxChunksToCheck}. * * @param chunkSize The chunk size after which a response check is executed. */ public MonitoringResponseOutOfOrderStrategy(final long chunkSize) { this(chunkSize, Long.MAX_VALUE); } /** * Instantiates a {@link MonitoringResponseOutOfOrderStrategy}. * * @param chunkSize The chunk size after which a response check is executed. * @param maxChunksToCheck The maximum number of chunks to check, allowing expensive checks to be avoided * after a sufficient portion of the request entity has been transferred. */ public MonitoringResponseOutOfOrderStrategy(final long chunkSize, final long maxChunksToCheck) { this.chunkSize = Args.positive(chunkSize, "chunkSize"); this.maxChunksToCheck = Args.positive(maxChunksToCheck, "maxChunksToCheck"); } @Override public boolean isEarlyResponseDetected( final ClassicHttpRequest request, final HttpClientConnection connection, final InputStream inputStream, final long totalBytesSent, final long nextWriteSize) throws IOException { if (nextWriteStartsNewChunk(totalBytesSent, nextWriteSize)) { final boolean ssl = connection.getSSLSession() != null; return ssl ? connection.isDataAvailable(Timeout.ONE_MILLISECOND) : (inputStream.available() > 0); } return false; } private boolean nextWriteStartsNewChunk(final long totalBytesSent, final long nextWriteSize) { final long currentChunkIndex = Math.min(totalBytesSent / chunkSize, maxChunksToCheck); final long newChunkIndex = Math.min((totalBytesSent + nextWriteSize) / chunkSize, maxChunksToCheck); return currentChunkIndex < newChunkIndex; } @Override public String toString() { return "DefaultResponseOutOfOrderStrategy{chunkSize=" + chunkSize + ", maxChunksToCheck=" + maxChunksToCheck + '}'; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/NoResponseOutOfOrderStrategy.java0100664 0000000 0000000 00000004503 14245617503 030571 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy; import java.io.InputStream; /** * An implementation of {@link ResponseOutOfOrderStrategy} which does not check for early responses. * * Early response detection requires 1ms blocking reads and incurs a hefty performance cost for * large uploads. * * @see MonitoringResponseOutOfOrderStrategy * @since 5.1 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class NoResponseOutOfOrderStrategy implements ResponseOutOfOrderStrategy { public static final NoResponseOutOfOrderStrategy INSTANCE = new NoResponseOutOfOrderStrategy(); @Override public boolean isEarlyResponseDetected( final ClassicHttpRequest request, final HttpClientConnection connection, final InputStream inputStream, final long totalBytesSent, final long nextWriteSize) { return false; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultBHttpClientConnection.java0100664 0000000 0000000 00000035002 14403631147 030501 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.util.Iterator; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.LengthRequiredException; import org.apache.hc.core5.http.NoHttpResponseException; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.UnsupportedHttpVersionException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.HttpMessageParser; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriter; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; import org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy; import org.apache.hc.core5.http.message.BasicTokenIterator; import org.apache.hc.core5.util.Args; /** * Default implementation of {@link HttpClientConnection}. * * @since 4.3 */ public class DefaultBHttpClientConnection extends BHttpConnectionBase implements HttpClientConnection { private final HttpMessageParser responseParser; private final HttpMessageWriter requestWriter; private final ContentLengthStrategy incomingContentStrategy; private final ContentLengthStrategy outgoingContentStrategy; private final ResponseOutOfOrderStrategy responseOutOfOrderStrategy; private volatile boolean consistent; /** * Creates new instance of DefaultBHttpClientConnection. * * @param http1Config Message http1Config. If {@code null} * {@link Http1Config#DEFAULT} will be used. * @param charDecoder decoder to be used for decoding HTTP protocol elements. * If {@code null} simple type cast will be used for byte to char conversion. * @param charEncoder encoder to be used for encoding HTTP protocol elements. * If {@code null} simple type cast will be used for char to byte conversion. * @param incomingContentStrategy incoming content length strategy. If {@code null} * {@link DefaultContentLengthStrategy#INSTANCE} will be used. * @param outgoingContentStrategy outgoing content length strategy. If {@code null} * {@link DefaultContentLengthStrategy#INSTANCE} will be used. * @param responseOutOfOrderStrategy response out of order strategy. If {@code null} * {@link NoResponseOutOfOrderStrategy#INSTANCE} will be used. * @param requestWriterFactory request writer factory. If {@code null} * {@link DefaultHttpRequestWriterFactory#INSTANCE} will be used. * @param responseParserFactory response parser factory. If {@code null} * {@link DefaultHttpResponseParserFactory#INSTANCE} will be used. */ public DefaultBHttpClientConnection( final Http1Config http1Config, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final ResponseOutOfOrderStrategy responseOutOfOrderStrategy, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { super(http1Config, charDecoder, charEncoder); this.requestWriter = (requestWriterFactory != null ? requestWriterFactory : DefaultHttpRequestWriterFactory.INSTANCE).create(); this.responseParser = (responseParserFactory != null ? responseParserFactory : DefaultHttpResponseParserFactory.INSTANCE).create(http1Config); this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy : DefaultContentLengthStrategy.INSTANCE; this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy : DefaultContentLengthStrategy.INSTANCE; this.responseOutOfOrderStrategy = responseOutOfOrderStrategy != null ? responseOutOfOrderStrategy : NoResponseOutOfOrderStrategy.INSTANCE; this.consistent = true; } /** * Creates new instance of DefaultBHttpClientConnection. * * @param http1Config Message http1Config. If {@code null} * {@link Http1Config#DEFAULT} will be used. * @param charDecoder decoder to be used for decoding HTTP protocol elements. * If {@code null} simple type cast will be used for byte to char conversion. * @param charEncoder encoder to be used for encoding HTTP protocol elements. * If {@code null} simple type cast will be used for char to byte conversion. * @param incomingContentStrategy incoming content length strategy. If {@code null} * {@link DefaultContentLengthStrategy#INSTANCE} will be used. * @param outgoingContentStrategy outgoing content length strategy. If {@code null} * {@link DefaultContentLengthStrategy#INSTANCE} will be used. * @param requestWriterFactory request writer factory. If {@code null} * {@link DefaultHttpRequestWriterFactory#INSTANCE} will be used. * @param responseParserFactory response parser factory. If {@code null} * {@link DefaultHttpResponseParserFactory#INSTANCE} will be used. */ public DefaultBHttpClientConnection( final Http1Config http1Config, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { this( http1Config, charDecoder, charEncoder, incomingContentStrategy, outgoingContentStrategy, null, requestWriterFactory, responseParserFactory); } public DefaultBHttpClientConnection( final Http1Config http1Config, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder) { this(http1Config, charDecoder, charEncoder, null, null, null, null); } public DefaultBHttpClientConnection(final Http1Config http1Config) { this(http1Config, null, null); } protected void onResponseReceived(final ClassicHttpResponse response) { } protected void onRequestSubmitted(final ClassicHttpRequest request) { } @Override public void bind(final Socket socket) throws IOException { super.bind(socket); } @Override public void sendRequestHeader(final ClassicHttpRequest request) throws HttpException, IOException { Args.notNull(request, "HTTP request"); final SocketHolder socketHolder = ensureOpen(); this.requestWriter.write(request, this.outbuffer, socketHolder.getOutputStream()); onRequestSubmitted(request); incrementRequestCount(); } @Override public void sendRequestEntity(final ClassicHttpRequest request) throws HttpException, IOException { Args.notNull(request, "HTTP request"); final SocketHolder socketHolder = ensureOpen(); final HttpEntity entity = request.getEntity(); if (entity == null) { return; } final long len = this.outgoingContentStrategy.determineLength(request); if (len == ContentLengthStrategy.UNDEFINED) { throw new LengthRequiredException(); } try (final OutputStream outStream = createContentOutputStream( len, this.outbuffer, new OutputStream() { final OutputStream socketOutputStream = socketHolder.getOutputStream(); final InputStream socketInputStream = socketHolder.getInputStream(); long totalBytes; void checkForEarlyResponse(final long totalBytesSent, final int nextWriteSize) throws IOException { if (responseOutOfOrderStrategy.isEarlyResponseDetected( request, DefaultBHttpClientConnection.this, socketInputStream, totalBytesSent, nextWriteSize)) { throw new ResponseOutOfOrderException(); } } @Override public void write(final byte[] b) throws IOException { checkForEarlyResponse(totalBytes, b.length); totalBytes += b.length; socketOutputStream.write(b); } @Override public void write(final byte[] b, final int off, final int len) throws IOException { checkForEarlyResponse(totalBytes, len); totalBytes += len; socketOutputStream.write(b, off, len); } @Override public void write(final int b) throws IOException { checkForEarlyResponse(totalBytes, 1); totalBytes++; socketOutputStream.write(b); } @Override public void flush() throws IOException { socketOutputStream.flush(); } @Override public void close() throws IOException { socketOutputStream.close(); } }, entity.getTrailers())) { entity.writeTo(outStream); } catch (final ResponseOutOfOrderException ex) { if (len > 0) { this.consistent = false; } } } @Override public boolean isConsistent() { return this.consistent; } @Override public void terminateRequest(final ClassicHttpRequest request) throws HttpException, IOException { Args.notNull(request, "HTTP request"); final SocketHolder socketHolder = ensureOpen(); final HttpEntity entity = request.getEntity(); if (entity == null) { return; } final Iterator ti = new BasicTokenIterator(request.headerIterator(HttpHeaders.CONNECTION)); while (ti.hasNext()) { final String token = ti.next(); if (HeaderElements.CLOSE.equalsIgnoreCase(token)) { this.consistent = false; return; } } final long len = this.outgoingContentStrategy.determineLength(request); if (len == ContentLengthStrategy.CHUNKED) { try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), entity.getTrailers())) { // just close } } else if (len >= 0 && len <= 1024) { try (final OutputStream outStream = createContentOutputStream(len, this.outbuffer, socketHolder.getOutputStream(), null)) { entity.writeTo(outStream); } } else { this.consistent = false; } } @Override public ClassicHttpResponse receiveResponseHeader() throws HttpException, IOException { final SocketHolder socketHolder = ensureOpen(); final ClassicHttpResponse response = this.responseParser.parse(this.inBuffer, socketHolder.getInputStream()); if (response == null) { throw new NoHttpResponseException("The target server failed to respond"); } final ProtocolVersion transportVersion = response.getVersion(); if (transportVersion != null && transportVersion.greaterEquals(HttpVersion.HTTP_2)) { throw new UnsupportedHttpVersionException(transportVersion); } this.version = transportVersion; onResponseReceived(response); final int status = response.getCode(); if (status < HttpStatus.SC_INFORMATIONAL) { throw new ProtocolException("Invalid response: " + status); } if (response.getCode() >= HttpStatus.SC_SUCCESS) { incrementResponseCount(); } return response; } @Override public void receiveResponseEntity( final ClassicHttpResponse response) throws HttpException, IOException { Args.notNull(response, "HTTP response"); final SocketHolder socketHolder = ensureOpen(); final long len = this.incomingContentStrategy.determineLength(response); response.setEntity(createIncomingEntity(response, this.inBuffer, socketHolder.getInputStream(), len)); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/ContentLengthOutputStream.java0100664 0000000 0000000 00000010622 14245617503 030152 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.OutputStream; import org.apache.hc.core5.http.StreamClosedException; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.apache.hc.core5.util.Args; /** * Output stream that cuts off after a defined number of bytes. This class * is used to send content of HTTP messages where the end of the content entity * is determined by the value of the {@code Content-Length header}. * Entities transferred using this stream can be maximum {@link Long#MAX_VALUE} * long. *

* Note that this class NEVER closes the underlying stream, even when * {@link #close()} gets called. Instead, the stream will be marked as closed * and no further output will be permitted. * * @since 4.0 */ public class ContentLengthOutputStream extends OutputStream { private final SessionOutputBuffer buffer; private final OutputStream outputStream; /** * The maximum number of bytes that can be written the stream. Subsequent * write operations will be ignored. */ private final long contentLength; /** Total bytes written */ private long total; /** True if the stream is closed. */ private boolean closed; /** * Default constructor. * * @param buffer Session output buffer * @param outputStream Output stream * @param contentLength The maximum number of bytes that can be written to * the stream. Subsequent write operations will be ignored. * * @since 4.0 */ public ContentLengthOutputStream(final SessionOutputBuffer buffer, final OutputStream outputStream, final long contentLength) { super(); this.buffer = Args.notNull(buffer, "Session output buffer"); this.outputStream = Args.notNull(outputStream, "Output stream"); this.contentLength = Args.notNegative(contentLength, "Content length"); } /** * Finishes writing to the underlying stream, but does NOT close the underlying stream. * @throws IOException If an I/O problem occurs. */ @Override public void close() throws IOException { if (!this.closed) { this.closed = true; this.buffer.flush(this.outputStream); } } @Override public void flush() throws IOException { this.buffer.flush(this.outputStream); } @Override public void write(final byte[] b, final int off, final int len) throws IOException { if (this.closed) { throw new StreamClosedException(); } if (this.total < this.contentLength) { final long max = this.contentLength - this.total; int chunk = len; if (chunk > max) { chunk = (int) max; } this.buffer.write(b, off, chunk, this.outputStream); this.total += chunk; } } @Override public void write(final byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(final int b) throws IOException { if (this.closed) { throw new StreamClosedException(); } if (this.total < this.contentLength) { this.buffer.write(b, this.outputStream); this.total++; } } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/DefaultConnectionReuseStrategy.java0100664 0000000 0000000 00000015654 14323605260 030531 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import java.util.Iterator; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicTokenIterator; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Default implementation of a strategy deciding about connection re-use. The strategy * determines whether a connection is persistent or not based on the message’s protocol * version and {@code Connection} header field if present. Connections will not be * re-used and will close if any of these conditions is met *

    *
  • the {@code close} connection option is present in the request message
  • *
  • the response message content body is incorrectly or ambiguously delineated
  • *
  • the {@code close} connection option is present in the response message
  • *
  • If the received protocol is {@code HTTP/1.0} (or earlier) and {@code keep-alive} * connection option is not present
  • *
* In the absence of a {@code Connection} header field, the non-standard but commonly used * {@code Proxy-Connection} header field will be used instead. If no connection options are * explicitly given the default policy for the HTTP version is applied. {@code HTTP/1.1} * (or later) connections are re-used by default. {@code HTTP/1.0} (or earlier) connections * are not re-used by default. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class DefaultConnectionReuseStrategy implements ConnectionReuseStrategy { public static final DefaultConnectionReuseStrategy INSTANCE = new DefaultConnectionReuseStrategy(); public DefaultConnectionReuseStrategy() { super(); } // see interface ConnectionReuseStrategy @Override public boolean keepAlive( final HttpRequest request, final HttpResponse response, final HttpContext context) { Args.notNull(response, "HTTP response"); if (request != null) { final Iterator ti = new BasicTokenIterator(request.headerIterator(HttpHeaders.CONNECTION)); while (ti.hasNext()) { final String token = ti.next(); if (HeaderElements.CLOSE.equalsIgnoreCase(token)) { return false; } } } // If a HTTP 204 No Content response contains a Content-length with value > 0 or Transfer-Encoding header, // don't reuse the connection. This is to avoid getting out-of-sync if a misbehaved HTTP server // returns content as part of a HTTP 204 response. if (response.getCode() == HttpStatus.SC_NO_CONTENT) { final Header clh = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); if (clh != null) { try { final long contentLen = Long.parseLong(clh.getValue()); if (contentLen > 0) { return false; } } catch (final NumberFormatException ex) { // fall through } } if (response.containsHeader(HttpHeaders.TRANSFER_ENCODING)) { return false; } } // Check for a self-terminating entity. If the end of the entity will // be indicated by closing the connection, there is no keep-alive. final Header teh = response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); if (teh != null) { if (!HeaderElements.CHUNKED_ENCODING.equalsIgnoreCase(teh.getValue())) { return false; } } else { final String method = request != null ? request.getMethod() : null; if (MessageSupport.canResponseHaveBody(method, response) && response.countHeaders(HttpHeaders.CONTENT_LENGTH) != 1) { return false; } } // Check for the "Connection" header. If that is absent, check for // the "Proxy-Connection" header. The latter is an unspecified and // broken but unfortunately common extension of HTTP. Iterator
headerIterator = response.headerIterator(HttpHeaders.CONNECTION); if (!headerIterator.hasNext()) { headerIterator = response.headerIterator("Proxy-Connection"); } final ProtocolVersion ver = response.getVersion() != null ? response.getVersion() : context.getProtocolVersion(); if (headerIterator.hasNext()) { if (ver.greaterEquals(HttpVersion.HTTP_1_1)) { final Iterator it = new BasicTokenIterator(headerIterator); while (it.hasNext()) { final String token = it.next(); if (HeaderElements.CLOSE.equalsIgnoreCase(token)) { return false; } } return true; } final Iterator it = new BasicTokenIterator(headerIterator); while (it.hasNext()) { final String token = it.next(); if (HeaderElements.KEEP_ALIVE.equalsIgnoreCase(token)) { return true; } } return false; } return ver.greaterEquals(HttpVersion.HTTP_1_1); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/DefaultAddressResolver.java0100664 0000000 0000000 00000004176 14334433176 027016 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import java.net.InetSocketAddress; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; /** * Default {@link HttpHost} to {@link InetSocketAddress} resolver. * * @since 5.0 */ public final class DefaultAddressResolver implements Resolver { public static final DefaultAddressResolver INSTANCE = new DefaultAddressResolver(); @Override public InetSocketAddress resolve(final HttpHost host) { if (host == null) { return null; } int port = host.getPort(); if (port < 0) { final String scheme = host.getSchemeName(); if (URIScheme.HTTP.same(scheme)) { port = 80; } else if (URIScheme.HTTPS.same(scheme)) { port = 443; } } return new InetSocketAddress(host.getHostName(), port); } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/ServerSupport.java0100664 0000000 0000000 00000006654 14245617503 025247 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.MethodNotSupportedException; import org.apache.hc.core5.http.MisdirectedRequestException; import org.apache.hc.core5.http.NotImplementedException; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.RequestHeaderFieldsTooLargeException; import org.apache.hc.core5.http.UnsupportedHttpVersionException; /** * HTTP Server support methods. * * @since 5.0 */ @Internal public class ServerSupport { public static void validateResponse( final HttpResponse response, final EntityDetails responseEntityDetails) throws HttpException { final int status = response.getCode(); switch (status) { case HttpStatus.SC_NO_CONTENT: case HttpStatus.SC_NOT_MODIFIED: if (responseEntityDetails != null) { throw new HttpException("Response " + status + " must not enclose an entity"); } } } public static String toErrorMessage(final Exception ex) { final String message = ex.getMessage(); return message != null ? message : ex.toString(); } public static int toStatusCode(final Exception ex) { final int code; if (ex instanceof MethodNotSupportedException) { code = HttpStatus.SC_NOT_IMPLEMENTED; } else if (ex instanceof UnsupportedHttpVersionException) { code = HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED; } else if (ex instanceof NotImplementedException) { code = HttpStatus.SC_NOT_IMPLEMENTED; } else if (ex instanceof RequestHeaderFieldsTooLargeException) { code = HttpStatus.SC_REQUEST_HEADER_FIELDS_TOO_LARGE; } else if (ex instanceof MisdirectedRequestException) { code = HttpStatus.SC_MISDIRECTED_REQUEST; } else if (ex instanceof ProtocolException) { code = HttpStatus.SC_BAD_REQUEST; } else { code = HttpStatus.SC_INTERNAL_SERVER_ERROR; } return code; } } httpcore5/src/main/java/org/apache/hc/core5/http/impl/EnglishReasonPhraseCatalog.java0100664 0000000 0000000 00000023050 14315123013 027552 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import java.util.Locale; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.ReasonPhraseCatalog; import org.apache.hc.core5.util.Args; /** * English reason phrases for HTTP status codes. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class EnglishReasonPhraseCatalog implements ReasonPhraseCatalog { // static array with english reason phrases defined below /** * The default instance of this catalog. * This catalog is thread safe, so there typically * is no need to create other instances. */ public final static EnglishReasonPhraseCatalog INSTANCE = new EnglishReasonPhraseCatalog(); /** * Restricted default constructor, for derived classes. * If you need an instance of this class, use {@link #INSTANCE INSTANCE}. */ protected EnglishReasonPhraseCatalog() { // no body } /** * Obtains the reason phrase for a status code. * * @param status the status code, in the range 100-599 * @param loc ignored * * @return the reason phrase, or {@code null} */ @Override public String getReason(final int status, final Locale loc) { Args.checkRange(status, 100, 599, "Unknown category for status code"); final int category = status / 100; final int subcode = status - 100*category; String reason = null; if (REASON_PHRASES[category].length > subcode) { reason = REASON_PHRASES[category][subcode]; } return reason; } /** Reason phrases lookup table. */ private static final String[][] REASON_PHRASES = new String[][]{ null, new String[4], // 1xx new String[27], // 2xx new String[9], // 3xx new String[52], // 4xx new String[12] // 5xx }; /** * Stores the given reason phrase, by status code. * Helper method to initialize the static lookup table. * * @param status the status code for which to define the phrase * @param reason the reason phrase for this status code */ private static void setReason(final int status, final String reason) { final int category = status / 100; final int subcode = status - 100*category; REASON_PHRASES[category][subcode] = reason; } // ----------------------------------------------------- Static Initializer /** Set up status code to "reason phrase" map. */ static { // HTTP 1.1 Server status codes -- see RFC 7231 setReason(HttpStatus.SC_OK, "OK"); setReason(HttpStatus.SC_CREATED, "Created"); setReason(HttpStatus.SC_ACCEPTED, "Accepted"); setReason(HttpStatus.SC_NO_CONTENT, "No Content"); setReason(HttpStatus.SC_MOVED_PERMANENTLY, "Moved Permanently"); setReason(HttpStatus.SC_MOVED_TEMPORARILY, "Moved Temporarily"); setReason(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); setReason(HttpStatus.SC_BAD_REQUEST, "Bad Request"); setReason(HttpStatus.SC_UNAUTHORIZED, "Unauthorized"); setReason(HttpStatus.SC_FORBIDDEN, "Forbidden"); setReason(HttpStatus.SC_NOT_FOUND, "Not Found"); setReason(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Internal Server Error"); setReason(HttpStatus.SC_NOT_IMPLEMENTED, "Not Implemented"); setReason(HttpStatus.SC_BAD_GATEWAY, "Bad Gateway"); setReason(HttpStatus.SC_SERVICE_UNAVAILABLE, "Service Unavailable"); setReason(HttpStatus.SC_CONTINUE, "Continue"); setReason(HttpStatus.SC_TEMPORARY_REDIRECT, "Temporary Redirect"); setReason(HttpStatus.SC_METHOD_NOT_ALLOWED, "Method Not Allowed"); setReason(HttpStatus.SC_CONFLICT, "Conflict"); setReason(HttpStatus.SC_PRECONDITION_FAILED, "Precondition Failed"); setReason(HttpStatus.SC_REQUEST_TOO_LONG, "Request Too Long"); setReason(HttpStatus.SC_REQUEST_URI_TOO_LONG, "Request-URI Too Long"); setReason(HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type"); setReason(HttpStatus.SC_MULTIPLE_CHOICES, "Multiple Choices"); setReason(HttpStatus.SC_SEE_OTHER, "See Other"); setReason(HttpStatus.SC_USE_PROXY, "Use Proxy"); setReason(HttpStatus.SC_PAYMENT_REQUIRED, "Payment Required"); setReason(HttpStatus.SC_NOT_ACCEPTABLE, "Not Acceptable"); setReason(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required"); setReason(HttpStatus.SC_REQUEST_TIMEOUT, "Request Timeout"); setReason(HttpStatus.SC_SWITCHING_PROTOCOLS, "Switching Protocols"); setReason(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, "Non Authoritative Information"); setReason(HttpStatus.SC_RESET_CONTENT, "Reset Content"); setReason(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); setReason(HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout"); setReason(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED, "Http Version Not Supported"); setReason(HttpStatus.SC_GONE, "Gone"); setReason(HttpStatus.SC_LENGTH_REQUIRED, "Length Required"); setReason(HttpStatus.SC_REQUESTED_RANGE_NOT_SATISFIABLE, "Requested Range Not Satisfiable"); setReason(HttpStatus.SC_EXPECTATION_FAILED, "Expectation Failed"); setReason(HttpStatus.SC_MISDIRECTED_REQUEST, "Misdirected Request"); // WebDAV Server-specific status codes setReason(HttpStatus.SC_PROCESSING, "Processing"); setReason(HttpStatus.SC_MULTI_STATUS, "Multi-Status"); setReason(HttpStatus.SC_ALREADY_REPORTED, "Already Reported"); setReason(HttpStatus.SC_IM_USED, "IM Used"); setReason(HttpStatus.SC_UNPROCESSABLE_ENTITY, "Unprocessable Entity"); setReason(HttpStatus.SC_INSUFFICIENT_SPACE_ON_RESOURCE, "Insufficient Space On Resource"); setReason(HttpStatus.SC_METHOD_FAILURE, "Method Failure"); setReason(HttpStatus.SC_LOCKED, "Locked"); setReason(HttpStatus.SC_INSUFFICIENT_STORAGE, "Insufficient Storage"); setReason(HttpStatus.SC_LOOP_DETECTED, "Loop Detected"); setReason(HttpStatus.SC_NOT_EXTENDED, "Not Extended"); setReason(HttpStatus.SC_FAILED_DEPENDENCY, "Failed Dependency"); setReason(HttpStatus.SC_TOO_EARLY, "Too Early"); setReason(HttpStatus.SC_UPGRADE_REQUIRED, "Upgrade Required"); // Additional HTTP Status Code - see RFC 6585 setReason(HttpStatus.SC_PRECONDITION_REQUIRED, "Precondition Required"); setReason(HttpStatus.SC_TOO_MANY_REQUESTS, "Too Many Requests"); setReason(HttpStatus.SC_REQUEST_HEADER_FIELDS_TOO_LARGE, "Request Header Fields Too Large"); setReason(HttpStatus.SC_NETWORK_AUTHENTICATION_REQUIRED, "Network Authentication Required"); // Early Hints - see RFC 8297 setReason(HttpStatus.SC_EARLY_HINTS, "Early Hints"); //Permanent Redirect - see RFC 7538 setReason(HttpStatus.SC_PERMANENT_REDIRECT, "Permanent Redirect"); // Legal Obstacles - see RFC 7725 setReason(HttpStatus.SC_UNAVAILABLE_FOR_LEGAL_REASONS, "Unavailable For Legal Reasons"); // Transparent Content Negotiation - see RFC 2295 setReason(HttpStatus.SC_VARIANT_ALSO_NEGOTIATES, "Variant Also Negotiates"); } } httpcore5/src/main/java/org/apache/hc/core5/http/RequestNotExecutedException.java0100664 0000000 0000000 00000003732 14323605260 027106 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.apache.hc.core5.annotation.Internal; /** * {@link ConnectionClosedException} subclass that signals requests cannot not be executed * due to the connection being closed. Generally requests failed with this exception * are safe to be re-executed. */ @Internal public class RequestNotExecutedException extends ConnectionClosedException { /** * Constructs a new RequestNotExecutedException with a default message. */ public RequestNotExecutedException() { super("Connection is closed"); } /** * Constructs a new RequestNotExecutedException with the specified detail message. * * @param message The exception detail message */ public RequestNotExecutedException(final String message) { super(message); } } httpcore5/src/main/java/org/apache/hc/core5/http/ReasonPhraseCatalog.java0100664 0000000 0000000 00000003411 14245617503 025314 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.util.Locale; /** * Interface for obtaining reason phrases for HTTP status codes. * * @since 4.0 */ public interface ReasonPhraseCatalog { /** * Obtains the reason phrase for a status code. * The optional context allows for catalogs that detect * the language for the reason phrase. * * @param status the status code, in the range 100-599 * @param loc the preferred locale for the reason phrase * * @return the reason phrase, or {@code null} if unknown */ String getReason(int status, Locale loc); } httpcore5/src/main/java/org/apache/hc/core5/http/ClassicHttpResponse.java0100664 0000000 0000000 00000002662 14245617503 025376 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.Closeable; /** * 'Classic' {@link HttpResponse} message that can enclose {@link HttpEntity}. * * @since 5.0 */ public interface ClassicHttpResponse extends HttpResponse, HttpEntityContainer, Closeable { // empty } httpcore5/src/main/java/org/apache/hc/core5/http/EndpointDetails.java0100664 0000000 0000000 00000006337 14245617503 024527 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.net.SocketAddress; import org.apache.hc.core5.net.InetAddressUtils; import org.apache.hc.core5.util.Timeout; /** * HTTP connection endpoint details. * * @since 5.0 */ public abstract class EndpointDetails implements HttpConnectionMetrics { private final SocketAddress remoteAddress; private final SocketAddress localAddress; private final Timeout socketTimeout; protected EndpointDetails(final SocketAddress remoteAddress, final SocketAddress localAddress, final Timeout socketTimeout) { this.remoteAddress = remoteAddress; this.localAddress = localAddress; this.socketTimeout = socketTimeout; } public SocketAddress getRemoteAddress() { return remoteAddress; } public SocketAddress getLocalAddress() { return localAddress; } /** * Gets the number of requests transferred over the connection, * 0 if not available. */ @Override public abstract long getRequestCount(); /** * Gets the number of responses transferred over the connection, * 0 if not available. */ @Override public abstract long getResponseCount(); /** * Gets the number of bytes transferred over the connection, * 0 if not available. */ @Override public abstract long getSentBytesCount(); /** * Gets the number of bytes transferred over the connection, * 0 if not available. */ @Override public abstract long getReceivedBytesCount(); /** * Gets the socket timeout. * * @return the socket timeout. */ public Timeout getSocketTimeout() { return socketTimeout; } @Override public String toString() { // Make enough room for two IPv6 addresses to avoid re-allocation in the StringBuilder. final StringBuilder buffer = new StringBuilder(90); InetAddressUtils.formatAddress(buffer, localAddress); buffer.append("<->"); InetAddressUtils.formatAddress(buffer, remoteAddress); return buffer.toString(); } } httpcore5/src/main/java/org/apache/hc/core5/http/MisdirectedRequestException.java0100664 0000000 0000000 00000003445 14245617503 027122 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Signals a misdirected request (the server is not authoritative to handle the request). * * @since 5.0 */ public class MisdirectedRequestException extends ProtocolException { private static final long serialVersionUID = 1L; /** * Creates an exception without a detail message. */ public MisdirectedRequestException() { super(); } /** * Creates an exception with the specified detail message. * * @param message The exception detail message */ public MisdirectedRequestException(final String message) { super(message); } } httpcore5/src/main/java/org/apache/hc/core5/http/RequestHeaderFieldsTooLargeException.java0100664 0000000 0000000 00000004213 14245617503 030634 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Signals request header field length or total field size violation. * * @since 5.0 */ public class RequestHeaderFieldsTooLargeException extends ProtocolException { private static final long serialVersionUID = 1L; /** * Creates a new RequestHeaderFieldsTooLargeException with the specified detail message. * * @param message The exception detail message */ public RequestHeaderFieldsTooLargeException(final String message) { super(message); } /** * Creates a new RequestHeaderFieldsTooLargeException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public RequestHeaderFieldsTooLargeException(final String message, final Throwable cause) { super(message, cause); } } httpcore5/src/main/java/org/apache/hc/core5/http/HttpException.java0100664 0000000 0000000 00000007621 14245617503 024234 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Signals that an HTTP exception has occurred. * * @since 4.0 */ public class HttpException extends Exception { private static final int FIRST_VALID_CHAR = Chars.SP; private static final long serialVersionUID = -5437299376222011036L; /** * Cleans the given String by converting characters with values less than 32 to equivalent hexadecimal codes. * * @param message the source string. * @return a converted string. */ static String clean(final String message) { final char[] chars = message.toCharArray(); int i; // First check to see if need to allocate a new StringBuilder for (i = 0; i < chars.length; i++) { if (chars[i] < FIRST_VALID_CHAR) { break; } } if (i == chars.length) { return message; } final StringBuilder builder = new StringBuilder(chars.length * 2); for (i = 0; i < chars.length; i++) { final char ch = chars[i]; if (ch < FIRST_VALID_CHAR) { builder.append("[0x"); final String hexString = Integer.toHexString(i); if (hexString.length() == 1) { builder.append("0"); } builder.append(hexString); builder.append("]"); } else { builder.append(ch); } } return builder.toString(); } /** * Creates a new HttpException with a {@code null} detail message. */ public HttpException() { super(); } /** * Creates a new HttpException with the specified detail message. * * @param message the exception detail message */ public HttpException(final String message) { super(clean(message)); } /** * Constructs a new HttpException with the specified detail message. * * @param format The exception detail message format; see {@link String#format(String, Object...)}. * @param args The exception detail message arguments; see {@link String#format(String, Object...)}. * * @since 5.0 */ public HttpException(final String format, final Object... args) { super(HttpException.clean(String.format(format, args))); } /** * Creates a new HttpException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public HttpException(final String message, final Throwable cause) { super(clean(message)); initCause(cause); } } httpcore5/src/main/java/org/apache/hc/core5/http/URIScheme.java0100664 0000000 0000000 00000003266 14245617503 023223 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.apache.hc.core5.util.Args; /** * Enumerates supported URI schemes. * * @since 5.0 */ public enum URIScheme { HTTP("http"), HTTPS("https"); public final String id; URIScheme(final String id) { this.id = Args.notBlank(id, "id"); } public String getId() { return id; } public boolean same(final String scheme) { return id.equalsIgnoreCase(scheme); } @Override public String toString() { return id; } } httpcore5/src/main/java/org/apache/hc/core5/http/NotImplementedException.java0100664 0000000 0000000 00000003431 14245617503 026234 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Signals an unsupported / unimplemented feature of the HTTP protocol. * * @since 4.0 */ public class NotImplementedException extends ProtocolException { private static final long serialVersionUID = 7929295893253266373L; /** * Creates an exception without a detail message. */ public NotImplementedException() { super(); } /** * Creates an exception with the specified detail message. * * @param message The exception detail message */ public NotImplementedException(final String message) { super(message); } } httpcore5/src/main/java/org/apache/hc/core5/http/ConnectionClosedException.java0100664 0000000 0000000 00000005325 14245617503 026545 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; /** * Signals that the connection has been closed unexpectedly. * * @since 4.0 */ public class ConnectionClosedException extends IOException { private static final long serialVersionUID = 617550366255636674L; /** * Constructs a new ConnectionClosedException with a default message. */ public ConnectionClosedException() { super("Connection is closed"); } /** * Constructs a new ConnectionClosedException with the specified detail message. * * @param message The exception detail message */ public ConnectionClosedException(final String message) { super(HttpException.clean(message)); } /** * Constructs a new ConnectionClosedException with the specified detail message. * * @param format The exception detail message format; see {@link String#format(String, Object...)}. * @param args The exception detail message arguments; see {@link String#format(String, Object...)}. * * @since 5.0 */ public ConnectionClosedException(final String format, final Object... args) { super(HttpException.clean(String.format(format, args))); } /** * Constructs a {@code ConnectionClosedException} with the specified detail message * and cause. * * @param message The exception detail message * @param cause The cause. * * @since 5.0 */ public ConnectionClosedException(final String message, final Throwable cause) { super(message, cause); } } httpcore5/src/main/java/org/apache/hc/core5/http/HttpRequestMapper.java0100664 0000000 0000000 00000003413 14245617503 025066 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.apache.hc.core5.http.protocol.HttpContext; /** * This class can be used to resolve an object matching a particular {@link HttpRequest}. * Usually the mapped object will be a request handler to process the request. * * @since 5.0 */ public interface HttpRequestMapper { /** * Resolves a handler matching the given request. * * @param request the request to map to a handler * @return HTTP request handler or {@code null} if no match * is found. */ T resolve(HttpRequest request, HttpContext context) throws HttpException; } httpcore5/src/main/java/org/apache/hc/core5/http/HttpHeaders.java0100664 0000000 0000000 00000015216 14403631147 023644 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; /** * Constants enumerating standard and common HTTP headers. * * @since 4.1 */ public final class HttpHeaders { private HttpHeaders() { // Don't allow instantiation. } public static final String ACCEPT = "Accept"; public static final String ACCEPT_CHARSET = "Accept-Charset"; public static final String ACCEPT_ENCODING = "Accept-Encoding"; public static final String ACCEPT_LANGUAGE = "Accept-Language"; public static final String ACCEPT_RANGES = "Accept-Ranges"; /** * The CORS {@code Access-Control-Allow-Credentials} response header field name. */ public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; /** * The CORS {@code Access-Control-Allow-Headers} response header field name. */ public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; /** * The CORS {@code Access-Control-Allow-Methods} response header field name. */ public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; /** * The CORS {@code Access-Control-Allow-Origin} response header field name. */ public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; /** * The CORS {@code Access-Control-Expose-Headers} response header field name. */ public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"; /** * The CORS {@code Access-Control-Max-Age} response header field name. */ public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age"; /** * The CORS {@code Access-Control-Request-Headers} request header field name. */ public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; /** * The CORS {@code Access-Control-Request-Method} request header field name. */ public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method"; public static final String AGE = "Age"; public static final String ALLOW = "Allow"; public static final String AUTHORIZATION = "Authorization"; public static final String CACHE_CONTROL = "Cache-Control"; public static final String CONNECTION = "Connection"; public static final String CONTENT_ENCODING = "Content-Encoding"; /** * The HTTP {@code Content-Disposition} header field name. */ public static final String CONTENT_DISPOSITION = "Content-Disposition"; public static final String CONTENT_LANGUAGE = "Content-Language"; public static final String CONTENT_LENGTH = "Content-Length"; public static final String CONTENT_LOCATION = "Content-Location"; public static final String CONTENT_MD5 = "Content-MD5"; public static final String CONTENT_RANGE = "Content-Range"; public static final String CONTENT_TYPE = "Content-Type"; /** * The HTTP {@code Cookie} header field name. */ public static final String COOKIE = "Cookie"; public static final String DATE = "Date"; public static final String DAV = "Dav"; public static final String DEPTH = "Depth"; public static final String DESTINATION = "Destination"; public static final String ETAG = "ETag"; public static final String EXPECT = "Expect"; public static final String EXPIRES = "Expires"; public static final String FROM = "From"; public static final String HOST = "Host"; public static final String IF = "If"; public static final String IF_MATCH = "If-Match"; public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; public static final String IF_NONE_MATCH = "If-None-Match"; public static final String IF_RANGE = "If-Range"; public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; public static final String KEEP_ALIVE = "Keep-Alive"; public static final String LAST_MODIFIED = "Last-Modified"; /** * The HTTP {@code Link} header field name. */ public static final String LINK = "Link"; public static final String LOCATION = "Location"; public static final String LOCK_TOKEN = "Lock-Token"; public static final String MAX_FORWARDS = "Max-Forwards"; public static final String OVERWRITE = "Overwrite"; public static final String PRAGMA = "Pragma"; public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; public static final String PROXY_CONNECTION = "Proxy-Connection"; public static final String RANGE = "Range"; public static final String REFERER = "Referer"; public static final String RETRY_AFTER = "Retry-After"; public static final String SERVER = "Server"; public static final String STATUS_URI = "Status-URI"; /** * The HTTP {@code Set-Cookie} header field name. */ public static final String SET_COOKIE = "Set-Cookie"; public static final String TE = "TE"; public static final String TIMEOUT = "Timeout"; public static final String TRAILER = "Trailer"; public static final String TRANSFER_ENCODING = "Transfer-Encoding"; public static final String UPGRADE = "Upgrade"; public static final String USER_AGENT = "User-Agent"; public static final String VARY = "Vary"; public static final String VIA = "Via"; public static final String WARNING = "Warning"; public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; } httpcore5/src/main/java/org/apache/hc/core5/io/0040775 0000000 0000000 00000000000 14245617503 020220 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/io/package-info.java0100664 0000000 0000000 00000002363 14245617503 023410 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core I/O component APIs and utilities. */ package org.apache.hc.core5.io; httpcore5/src/main/java/org/apache/hc/core5/io/ModalCloseable.java0100664 0000000 0000000 00000003256 14245617503 023734 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.io; import java.io.Closeable; /** * Process or endpoint that can be closed either immediately or gracefully. * * @since 5.0 */ public interface ModalCloseable extends Closeable { /** * Closes this process or endpoint and releases any system resources associated * with it. If the endpoint or the process is already closed then invoking this * method has no effect. * * @param closeMode How to close the receiver. */ void close(CloseMode closeMode); } httpcore5/src/main/java/org/apache/hc/core5/io/Closer.java0100664 0000000 0000000 00000004476 14245617503 022322 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.io; import java.io.Closeable; import java.io.IOException; /** * Closes resources. * * @since 5.0 */ public final class Closer { /** * Closes the given Closeable in a null-safe manner. * * @param closeable what to close. * @throws IOException */ public static void close(final Closeable closeable) throws IOException { if (closeable != null) { closeable.close(); } } /** * Closes the given Closeable in a null-safe manner. * * @param closeable what to close. * @param closeMode How to close the given resource. */ public static void close(final ModalCloseable closeable, final CloseMode closeMode) { if (closeable != null) { closeable.close(closeMode); } } /** * Closes the given Closeable quietly in a null-safe manner even in the event of an exception. * * @param closeable what to close. */ public static void closeQuietly(final Closeable closeable) { try { close(closeable); } catch (final IOException e) { // Quietly ignore } } } httpcore5/src/main/java/org/apache/hc/core5/io/SocketTimeoutExceptionFactory.java0100664 0000000 0000000 00000003517 14245617503 027074 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.io; import java.net.SocketTimeoutException; import java.util.Objects; import org.apache.hc.core5.util.Timeout; /** * Creates {@link SocketTimeoutException} instances. * * @since 5.0 */ public final class SocketTimeoutExceptionFactory { /** * Creates a new {@link SocketTimeoutException} with a message for the given timeout. * * @param timeout * the timeout value. * @return a new {@link SocketTimeoutException} with a message for the given timeout. */ static public SocketTimeoutException create(final Timeout timeout) { return new SocketTimeoutException(Objects.toString(timeout)); } } httpcore5/src/main/java/org/apache/hc/core5/io/IOCallback.java0100664 0000000 0000000 00000002546 14245617503 023013 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.io; import java.io.IOException; /** * Abstract I/O callback. * * @since 5.0 */ public interface IOCallback { void execute(T object) throws IOException; } httpcore5/src/main/java/org/apache/hc/core5/io/CloseMode.java0100664 0000000 0000000 00000002450 14245617503 022733 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.io; /** * Close operation mode. * * @since 5.0 */ public enum CloseMode { IMMEDIATE, GRACEFUL } httpcore5/src/main/java/org/apache/hc/core5/concurrent/0040775 0000000 0000000 00000000000 14403631147 021767 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/concurrent/package-info.java0100664 0000000 0000000 00000002365 14245617503 025165 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core concurrency component APIs. */ package org.apache.hc.core5.concurrent; httpcore5/src/main/java/org/apache/hc/core5/concurrent/FutureContribution.java0100664 0000000 0000000 00000003617 14245617503 026514 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; /** * Convenience base class for {@link FutureCallback}s that contribute a result * of the operation to another {@link BasicFuture}. * * @param the future result type of an asynchronous operation. * @since 5.1 */ public abstract class FutureContribution implements FutureCallback { private final BasicFuture future; public FutureContribution(final BasicFuture future) { this.future = future; } @Override public final void failed(final Exception ex) { if (future != null) { future.failed(ex); } } @Override public final void cancelled() { if (future != null) { future.cancel(); } } } httpcore5/src/main/java/org/apache/hc/core5/concurrent/CancellableDependency.java0100664 0000000 0000000 00000003351 14245617503 027021 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; /** * This interface represents {@link Cancellable} object dependent on another * ongoing process or operation. * * @since 5.0 */ public interface CancellableDependency extends Cancellable { /** * Sets {@link Cancellable} dependency on another ongoing process or * operation represented by {@link Cancellable}. */ void setDependency(Cancellable cancellable); /** * Determines whether the process or operation has been cancelled. * * @return cancelled flag. */ boolean isCancelled(); } httpcore5/src/main/java/org/apache/hc/core5/concurrent/FutureCallback.java0100664 0000000 0000000 00000003007 14245617503 025522 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; /** * A callback interface that gets invoked upon completion of * a {@link java.util.concurrent.Future}. * * @param the future result type returned by this callback. * @since 4.2 */ public interface FutureCallback { void completed(T result); void failed(Exception ex); void cancelled(); } httpcore5/src/main/java/org/apache/hc/core5/concurrent/CompletedFuture.java0100664 0000000 0000000 00000004362 14245617503 025747 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Immutable, completed future. * * @param the future result type of an asynchronous operation. * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class CompletedFuture implements Future, Cancellable { private final T result; public CompletedFuture(final T result) { super(); this.result = result; } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return true; } @Override public T get() { return this.result; } @Override public T get(final long timeout, final TimeUnit unit) { return this.result; } @Override public boolean cancel(final boolean mayInterruptIfRunning) { return false; } @Override public boolean cancel() { return false; } } httpcore5/src/main/java/org/apache/hc/core5/concurrent/ComplexCancellable.java0100664 0000000 0000000 00000005222 14245617503 026351 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; import java.util.concurrent.atomic.AtomicMarkableReference; import org.apache.hc.core5.util.Args; /** * {@link Cancellable} that has a dependency on another {@link Cancellable} * process or operation. Dependent process or operation will get cancelled * if this {@link Cancellable} itself is cancelled. * * @since 5.0 */ public final class ComplexCancellable implements CancellableDependency { private final AtomicMarkableReference dependencyRef; public ComplexCancellable() { this.dependencyRef = new AtomicMarkableReference<>(null, false); } @Override public boolean isCancelled() { return dependencyRef.isMarked(); } @Override public void setDependency(final Cancellable dependency) { Args.notNull(dependency, "dependency"); final Cancellable actualDependency = dependencyRef.getReference(); if (!dependencyRef.compareAndSet(actualDependency, dependency, false, false)) { dependency.cancel(); } } @Override public boolean cancel() { while (!dependencyRef.isMarked()) { final Cancellable actualDependency = dependencyRef.getReference(); if (dependencyRef.compareAndSet(actualDependency, actualDependency, false, true)) { if (actualDependency != null) { actualDependency.cancel(); } return true; } } return false; } } httpcore5/src/main/java/org/apache/hc/core5/concurrent/BasicFuture.java0100664 0000000 0000000 00000011766 14245617503 025062 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeoutValueException; /** * Basic implementation of the {@link Future} interface. {@code BasicFuture} * can be put into a completed state by invoking any of the following methods: * {@link #cancel()}, {@link #failed(Exception)}, or {@link #completed(Object)}. * * @param the future result type of an asynchronous operation. * @since 4.2 */ public class BasicFuture implements Future, Cancellable { private final FutureCallback callback; private volatile boolean completed; private volatile boolean cancelled; private volatile T result; private volatile Exception ex; public BasicFuture(final FutureCallback callback) { super(); this.callback = callback; } @Override public boolean isCancelled() { return this.cancelled; } @Override public boolean isDone() { return this.completed; } private T getResult() throws ExecutionException { if (this.ex != null) { throw new ExecutionException(this.ex); } if (cancelled) { throw new CancellationException(); } return this.result; } @Override public synchronized T get() throws InterruptedException, ExecutionException { while (!this.completed) { wait(); } return getResult(); } @Override public synchronized T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { Args.notNull(unit, "Time unit"); final long msecs = unit.toMillis(timeout); final long startTime = (msecs <= 0) ? 0 : System.currentTimeMillis(); long waitTime = msecs; if (this.completed) { return getResult(); } else if (waitTime <= 0) { throw TimeoutValueException.fromMilliseconds(msecs, msecs + Math.abs(waitTime)); } else { for (;;) { wait(waitTime); if (this.completed) { return getResult(); } waitTime = msecs - (System.currentTimeMillis() - startTime); if (waitTime <= 0) { throw TimeoutValueException.fromMilliseconds(msecs, msecs + Math.abs(waitTime)); } } } } public boolean completed(final T result) { synchronized(this) { if (this.completed) { return false; } this.completed = true; this.result = result; notifyAll(); } if (this.callback != null) { this.callback.completed(result); } return true; } public boolean failed(final Exception exception) { synchronized(this) { if (this.completed) { return false; } this.completed = true; this.ex = exception; notifyAll(); } if (this.callback != null) { this.callback.failed(exception); } return true; } @Override public boolean cancel(final boolean mayInterruptIfRunning) { synchronized(this) { if (this.completed) { return false; } this.completed = true; this.cancelled = true; notifyAll(); } if (this.callback != null) { this.callback.cancelled(); } return true; } @Override public boolean cancel() { return cancel(true); } } httpcore5/src/main/java/org/apache/hc/core5/concurrent/Cancellable.java0100664 0000000 0000000 00000003156 14245617503 025025 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; /** * A {@code Cancellable} represents a process or an operation that can be * canceled. * * @since 4.2 */ public interface Cancellable { /** * Cancels the ongoing operation or process. * * @return {@code true} if the operation or process has been cancelled as a result of * this method call or {@code false} if it has already been cancelled or not started. */ boolean cancel(); } httpcore5/src/main/java/org/apache/hc/core5/concurrent/CallbackContribution.java0100664 0000000 0000000 00000003657 14245617503 026742 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; /** * Convenience base class for {@link FutureCallback}s that contribute a result * of the operation to another {@link FutureCallback}. * * @param the future result type of an asynchronous operation. * @since 5.1 */ public abstract class CallbackContribution implements FutureCallback { private final FutureCallback callback; public CallbackContribution(final FutureCallback callback) { this.callback = callback; } @Override public final void failed(final Exception ex) { if (callback != null) { callback.failed(ex); } } @Override public final void cancelled() { if (callback != null) { callback.cancelled(); } } } httpcore5/src/main/java/org/apache/hc/core5/concurrent/ComplexFuture.java0100664 0000000 0000000 00000006214 14403631147 025434 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.util.Args; /** * {@link Future} whose result depends on another {@link Cancellable} process * or operation or another {@link Future}. Dependent process will get cancelled * if the future itself is cancelled. * * @param the future result type of an asynchronous operation. * @since 5.0 */ public final class ComplexFuture extends BasicFuture implements CancellableDependency { private final AtomicReference dependencyRef; public ComplexFuture(final FutureCallback callback) { super(callback); this.dependencyRef = new AtomicReference<>(); } @Override public void setDependency(final Cancellable dependency) { Args.notNull(dependency, "dependency"); if (isDone()) { dependency.cancel(); } else { dependencyRef.set(dependency); } } public void setDependency(final Future dependency) { Args.notNull(dependency, "dependency"); if (dependency instanceof Cancellable) { setDependency((Cancellable) dependency); } else { setDependency(() -> dependency.cancel(true)); } } @Override public boolean completed(final T result) { final boolean completed = super.completed(result); dependencyRef.set(null); return completed; } @Override public boolean failed(final Exception exception) { final boolean failed = super.failed(exception); dependencyRef.set(null); return failed; } @Override public boolean cancel(final boolean mayInterruptIfRunning) { final boolean cancelled = super.cancel(mayInterruptIfRunning); final Cancellable dependency = dependencyRef.getAndSet(null); if (dependency != null) { dependency.cancel(); } return cancelled; } } httpcore5/src/main/java/org/apache/hc/core5/concurrent/DefaultThreadFactory.java0100664 0000000 0000000 00000004430 14245617503 026700 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; /** * Default {@link ThreadFactory} implementation. * * @since 5.0 */ public class DefaultThreadFactory implements ThreadFactory { private final String namePrefix; private final ThreadGroup group; private final AtomicLong count; private final boolean daemon; public DefaultThreadFactory(final String namePrefix, final ThreadGroup group, final boolean daemon) { this.namePrefix = namePrefix; this.group = group; this.daemon = daemon; this.count = new AtomicLong(); } public DefaultThreadFactory(final String namePrefix, final boolean daemon) { this(namePrefix, null, daemon); } public DefaultThreadFactory(final String namePrefix) { this(namePrefix, null, false); } @Override public Thread newThread(final Runnable target) { final Thread thread = new Thread(this.group, target, this.namePrefix + "-" + this.count.incrementAndGet()); thread.setDaemon(daemon); return thread; } } httpcore5/src/main/java/org/apache/hc/core5/function/0040775 0000000 0000000 00000000000 14403631147 021432 5ustar000000000 0000000 httpcore5/src/main/java/org/apache/hc/core5/function/package-info.java0100664 0000000 0000000 00000002367 14245617503 024632 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Commonly used functional interfaces. */ package org.apache.hc.core5.function; httpcore5/src/main/java/org/apache/hc/core5/function/Supplier.java0100664 0000000 0000000 00000002715 14403631147 024102 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.function; /** * Represents a supplier of results. * * @param the type of results supplied by this supplier. * @since 5.0 */ @FunctionalInterface public interface Supplier { /** * Gets a result. * * @return a result */ T get(); } httpcore5/src/main/java/org/apache/hc/core5/function/Resolver.java0100664 0000000 0000000 00000002540 14403631147 024074 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.function; /** * Abstract resolver from input to output. * * @since 5.0 */ @FunctionalInterface public interface Resolver { O resolve(I object); } httpcore5/src/main/java/org/apache/hc/core5/function/Factory.java0100664 0000000 0000000 00000002522 14403631147 023702 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.function; /** * Abstract object factory. * * @since 5.0 */ @FunctionalInterface public interface Factory { T create(P parameter); } httpcore5/src/main/java/org/apache/hc/core5/function/Callback.java0100664 0000000 0000000 00000002513 14403631147 023767 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.function; /** * Abstract callback. * * @since 5.0 */ @FunctionalInterface public interface Callback { void execute(T object); } httpcore5/src/main/java/org/apache/hc/core5/function/Decorator.java0100664 0000000 0000000 00000002513 14403631147 024215 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.function; /** * Abstract decorator. * * @since 5.0 */ @FunctionalInterface public interface Decorator { T decorate(T object); } httpcore5/src/test/0040775 0000000 0000000 00000000000 14245617503 013304 5ustar000000000 0000000 httpcore5/src/test/resources/0040775 0000000 0000000 00000000000 14245617503 015316 5ustar000000000 0000000 httpcore5/src/test/resources/test-server.p120100664 0000000 0000000 00000011717 14245617503 020131 0ustar000000000 0000000 00 *H uq0m0i *H ZV0R0N *H  00) *H  0Mi"V/ƉPju)f)0t~{Y|xJa7A[zJ(6st_^C 6/ApLJcG jOa Vzص3w|Wu7Xz0M~G7#{/Lp[tK_ V%Kb.E݉o;6Z*9 bYeZ.&ύ).*tXa,7ږ] ridHy4eMYaQ?>Vf> "nLu\}r!7Cm$d˰\\hѴKXZFQ3SR٣˜-!9p/.~mT7iLX-cI8-=5ʲ鈺5aeܹyIBTJyMOYѿ k)1`AfDGC1IgT5WdoWdboj& ;ϨE#Ra#V{_!1h\1݁79M"PG*%Q;_>5JQLÔȐh>\@?[NJ04R~ivmʷZ}Ŋ7sHtsMK'ge;e' c}>zC([bdu$ONjԫ8k-t3ڡUC6>|]l!#ݜMԸ>͞%Ty4Eg\s3j#;( ?96F-f<)w ilr^`: /{"zQN6:ƭ1@0 *H  1 server0! *H  1Time 15708094235680  *H  0 0  *H 0) *H  0U* nJؼ,+#P zI%?$Rx{Rnʔ^DĠW/Զ~s&"[Ofslc[#^,"*5T~sd w|E$QLߔ4hg4XߒQпp{EQ2UR*x2IwsOF@3Q}DD:?<^ 6aV '$~2+;:4;^XjK.2~uۻє6K붴IC"iKŕ*`!Q,\|&w3/SlS;_Dq7敀ls3/q*H>4 @'_;nc!;W6¿,Vps"r:'\k$ay3g%&!Ho;z_i[kCO|C9Qui@?tk=QT扽`3c FNHcD+g8iݕĮ`cز: _8et QG٣~ᙼ%\k3h'zqgY:s FX48Ujָg/Q8 :&B=d 704#-$E-v"rU Ž,mD_;6d)hN9hrKt^|] @Z [~`gΕă@|q/Vfg:)gC9SkmZXE:j ؚGo7 p4fk#Oļfcb ifit@7:/i(~`ğ^rhOB(DNvpm%xݯA%V+?+ yX*r^ TѠ vJek,g`u֜/} % qdtAB6#L2ΎeJaǫF쁊Fgo Vz!@4j-:85 c۷`t$7pcNo b9A)@*2w/]Tͮp=&=QHWegV#2'\4AcK k.ʦ] .l5wF9$!pN!Vu9αy$=%uzA NtK^Qq mc,5,怘jNѓ0-*7)X=~DjTmrQC8Y h]~st\.s9 ѷ|V~9M`yߏ]ú[=dI 5>G8ȺI0_S WW:Enc" uklmXMgc18 XrL)8p(Q&p?$!p?=lƧdrfV$ >Xʿ?CZx̿fv`z/aTE{vJJkZ`Դzb9-qܖ?{v?;SUɾ]BGk~9b@O>]c( }PKf)9qN<$ Cf[dWR`X|,yp}e$w|ʕ~H,mg0 D\=,ho:M<p U-I%NJcܙ1`>2iH{f_9 IQZ)C4(”DO6npє)6SfQr$ztmẵ2b&cO餡&j/npb& ^1 yXjnU"?,!? &lS/9~+u6 .ThiͬpiιC|f~GW ESf su 碞^OA >qC{8詯'YEṟXZ+ϋ1OJFdi%^çtQ㽮ztrQ@3YU8ܺq9C,AYĩ-)*bRO&վd ac&"XeF=m` |? `hp,w_~'$51vWf/ xTy:>FQE:n̈́~)yIvY]~,I;ٴm5M3<6S!qkegjqmsX_G}Gy USW n-y(E[eHEtD oGptC?W4IP|B&,OXVM3e||8I5fV/kuĊd$w.؍fXNο7G%(ja_pPKRm_!K"^ h~>[}`WjwХP2!wTv!)JgВd~q6q^JB>h-Ͷhng޹&~O=j:EA85KoU|4[f'm./Hkul @eB 7ox'ϙm\3!L&F^(2{Q|Dgk-uVSeܞE{?l- iP1ߘM\-ty0qCS #vFCn?\f8͇ ^m XeW#qv߇`XdPz"2tXlGB/W=`թ"BCTѯ{4 * I欯=HW[0_Vg{4]4SŒToYQ r^:^=XQKFAC.2` &H wj<` ݲ~K8b<1oe\"5me~LчUb.4&+TօMݼƀ5Կc[+UV20b Y0>0!0 + (5df|N(A?` ]dhttpcore5/src/test/resources/test.p120100664 0000000 0000000 00000005077 14245617503 016627 0ustar000000000 0000000 0 ;0  *H   0 0 *H rn0j0f *H  00) *H  0`4ALI2vP)Thʡ{c'$jTkN|Gh^o#qA+kw|]"ơQ`'^Ja#6"y?1CR CS!Hn _77hJ_G%(`GI?C?¤GS<#9w?_&TXf(qKW9Z~ppy0Sa3/Jnj;QxcKъ^Lbӂ @[\Q"+˻2lt^j~qNSz24T_ )f"wW-P>bȻ`&., tq[S_ԇڍ1E+]Զ]LtdoVZf-@|`T#|nf:D16[y^|`qVC^OV(dQ]gh8!ɊcNCSһK*&'G# }xPc;cv9jT/`x3JG'gO`$γE(lp`Kߡl^ظ:Ⲑ%~7C ^#/oF] :6E//zcBkViEd"vb$)|UF͗%# "ASEȵ24`t: jFrٞ@ D0|aANvlWzz'g]v~&)]cWlCǼ Ū?GdDdɑR8ڄX=/ xpS1ܢN%ߋх"7'o#U^)em:YwfH'ӽ3 !<PNh >\/#+K -{;h+Gʈnם6U<\7-눌C$gJ7wطi??WF)l9οpo}\CAz1 ZW-f!ʰbT 2-=$@8͚#h,Æ:W:6zZDJ$3/FF\&S /+`QU A197 l4e.54{wBJi,1X03 *H  1&$simple-http-server0! *H  1Time 15708083005120T *H E0A0: *H 0) *H  0:Cztr;!K ,Pe4-3m!DwހZBݪa5W ݲF;%C 0F}B2g.2S)V㊲_m qWQ+`cf0Y{ <ӌzDt@lO& CNJ) Ssʗh*R9iQ ]3kZ 7(Kl#GCc/q*~aYHLy+Z5Bpn\-/~cQ5ڴ9IcZ#6EғB&_t3IӼTP,J[ D0c`t2;5F4$uERui&tﳹ74<~jTmseѵipi7 zvc}Q!SrZKgA}j~`dxzMXr ҷ> |N.d .sc?9>1ԛÃmG}[@'v&X)"S@q>dzD 1x$$R{ꯀFt4hApf>r.|KcJ;r 0t!Vtip ѤK/n)( Eiv*0T9BѦ}QlPNKjlYBM y!|ҰL'CSi 0!0 +Hb;`wA|˪,q41xcPdBOڛ2httpcore5/src/test/resources/test-keypasswd.p120100664 0000000 0000000 00000005077 14245617503 020637 0ustar000000000 0000000 0 ;0  *H   0 0 *H rn0j0f *H  00) *H  0 2eVV>k=,eg13GY}Y#=5ZfiLP(xFNdk |&d(~uda[%郬j 9]JDp%M1b}z6>cI 3TB͏rbF NCP^%@40{& dž0GREXm1yxJv= '$pT73a"&4(Ț+1%M?"P@jaz8l~VO;|klzG_;1 Ge4lCgEZVS=enL&Ո/6T-7_V'Ojz i~QHM '͎ڂ1EzE^ϼe7D$%c6qAfvQhY\v6os6Jc Y ̍}4 yjv~o%K\1X03 *H  1&$simple-http-server0! *H  1Time 15708090146480T *H E0A0: *H 0) *H  0 C$A~Cۧ-P΢,(lʵCք@^G 26 :3X_gqN%B->h ^Wsp-:qkq֙TqF/_aڱi 0թ3$u7A.'\ Ka[w?x7$a/qF34fW|>It;JqMq$M V5pF~fo|P۔s;ʠc)PGXC{yn E Ϊn+:yPwWJ4HbV֭0!0 +1+Ø/:V+eg <. *** Use Java 11 or older *** == generate test key store with a self signed key --- keytool -genkey \ -keystore test.12 -storepass nopassword \ -keyalg RSA -keysize 2048 \ -alias simple-http-server \ -validity 100000 \ -dname "CN=localhost, OU=Apache HttpComponents, O=Apache Software Foundation" \ -ext SAN="DNS:localhost" --- == generate test key store with a self signed key protected with a key password --- keytool -genkey \ -keystore test-keypasswd.p12 -storepass nopassword \ -keyalg RSA -keysize 2048 \ -alias simple-http-server \ -validity 100000 \ -dname "CN=localhost, OU=Apache HttpComponents, O=Apache Software Foundation" \ -ext SAN="DNS:localhost" --- == generate test CA --- keytool -genkeypair \ -keystore ca.p12 -storepass nopassword \ -keyalg RSA -keysize 2048 \ -alias ca \ -validity 100000 \ -dname "EMAILADDRESS=dev@hc.apache.org, CN=Test CA, OU=HttpComponents Project, O=Apache Software Foundation" \ -ext KeyUsage:critical="keyCertSign" \ -ext BasicConstraints:critical="ca:true" \ -ext SAN="EMAIL:dev@hc.apache.org" --- == export test CA certificate --- keytool -export \ -keystore ca.p12 -storepass nopassword \ -alias ca \ -file test-ca.crt \ -rfc --- == generate test server key --- keytool -genkeypair \ -keystore test-server.p12 -storepass nopassword \ -keyalg RSA -keysize 2048 \ -alias server \ -validity 100000 \ -dname "CN=Test Server, OU=HttpComponents Project, O=Apache Software Foundation" --- == create server certificate signing request --- keytool -certreq \ -keystore test-server.p12 -storepass nopassword \ -alias server \ -file server.csr --- == sign server certificate --- keytool -gencert \ -keystore ca.p12 -storepass nopassword \ -alias ca \ -validity 100000 \ -infile server.csr \ -outfile server.crt \ -ext KeyUsage:critical="digitalSignature,keyEncipherment" \ -ext EKU="serverAuth" \ -ext SAN="DNS:localhost" \ -rfc --- == import CA root certificate and signed server certificate --- keytool -importcert \ -keystore test-server.p12 -storepass nopassword \ -file test-ca.crt \ -alias caroot --- keytool -importcert \ -keystore test-server.p12 -storepass nopassword \ -file server.crt \ -alias server --- == generate client keys --- keytool -genkeypair \ -keystore test-client.p12 -storepass nopassword \ -keyalg RSA -keysize 2048 \ -alias client1 \ -validity 100000 \ -dname "CN=Test Client 1, OU=HttpComponents Project, O=Apache Software Foundation" --- keytool -genkeypair \ -keystore test-client.p12 -storepass nopassword \ -keyalg RSA -keysize 2048 \ -alias client2 \ -validity 100000 \ -dname "CN=Test Client 2, OU=HttpComponents Project, O=Apache Software Foundation" --- == create client certificate signing requests --- keytool -certreq \ -keystore test-client.p12 -storepass nopassword \ -alias client1 \ -file client1.csr --- keytool -certreq \ -keystore test-client.p12 -storepass nopassword \ -alias client2 \ -file client2.csr --- == sign client certificates --- keytool -gencert \ -keystore ca.p12 -storepass nopassword \ -alias ca \ -validity 100000 \ -infile client1.csr \ -outfile client1.crt \ -ext EKU="clientAuth" \ -ext SAN="EMAIL:test-client-1@hc.apache.org" \ -rfc --- keytool -gencert \ -keystore ca.p12 -storepass nopassword \ -alias ca \ -validity 100000 \ -infile client2.csr \ -outfile client2.crt \ -ext EKU="clientAuth" \ -ext SAN="EMAIL:test-client-2@hc.apache.org" \ -rfc --- == import CA root certificate and signed server certificate --- keytool -importcert \ -keystore test-client.p12 -storepass nopassword \ -file test-ca.crt \ -alias caroot --- keytool -importcert \ -keystore test-client.p12 -storepass nopassword \ -file client1.crt \ -alias client1 --- keytool -importcert \ -keystore test-client.p12 -storepass nopassword \ -file client2.crt \ -alias client2 --- httpcore5/src/test/resources/test-client.p120100664 0000000 0000000 00000021215 14245617503 020073 0ustar000000000 0000000 0"0"B *H "3"/0"+0  *H   0 0P *H  00) *H  0/}Հz]O{FP3Wg. AdJHGM[ζ-He/ѵ͜tb e h8+E2ް=^EDyRL^9w}&oN'+ᾠ+gCBś#>@b &DsUax~m0&R.2xÇi08h@[n0̀b N=\#D1J yV kMLR#rwm$BΦUݦ`]"Ē*\y6w(Q":ظhs Ԫ!=Dc1B0 *H  1client10! *H  1Time 15708095392920P *H  00) *H  0N7o35W4LP8w9c(t&_&3l/E'h'şQ~3ҬTDkf"I{g4&Ehdĸ pz >#py~.Yz5rH,%J}SP)^༶Q5MvpVpbʮ|t@ieC]>:8Tvr{69ܸ9c5!ܝz |!Hr5S2A"G΄-nq^XEKnC! YWJCӖf2KA!(@T'](> Ը v/c{ a pLuD10KBeFbaڏ 5L m1.xL>(%;iBc=Z-IJ_c*B XA2N9ͩԃ}–`[Ba!h7lZPe3%\,<સ`/G 'BWMSٱCF|98@~sYW?1˴3r@_E{U'7eQg+aClK8v XIpp[R-2J9,T{H+A M&F tR頨&$*;2K؏< qRmIDWg,"ku4ĀyFHh-f?`3@#W`F1IrE޽{xb9v8Pa~G{VivhxM.˜iy^W6wȽatf c[WS@<:GdMA/VO\A5SI5S [Z הH&.s:?}+{8 /Mr–]&Nl@ݱ]LNio2;jxǂOјg;e" uRh7Ht{ᗵMMSXR:GVmMV܏ͻfɍ,e*+1`JTI]ߗ__yw (5h/f2B 47"hi)r.~S?v[-Wӊpt4kfW r*P(j޺P*AY޳dFy}!֏ҙuxjܰa[O^fcnpwG0Uì1B0 *H  1client20! *H  1Time 15708095451480d *H U0Q0J *H 0) *H  0xykʡR^(4XoEPVtn]2%IAK` \ZeV˞WAY =}(.&^)؍`>7*ŎM sH=>%='Vz<1b<|P p6w$ 0a̮p.4x&H&Y/1͎ۏe_5c0eSs'۷"__L1LbtaS'KpVB:PZ{^ /KsleY0#q09 !F.F5t+o09.4`I$Ђ qۭ ĄGtnؠ ʅS߹:*RLͺ EXp8_W8q_b-a&ru):Jn)E{9s4{+)-5rLL<.`O䰘&<ӳܸv,Ҝ(PN}&=b5w~NBCwrI,Lۦf`Rۙ=|̊Sá {d@HӝןM'w7%~K t\/N.:$z]/3&mTunnŁ#f84 O]'J^L;ֲx!M!;Gå#?!Ǹ֯'ΆUBpxm"MfhZI12S MDrt?]ujP(E䛇&$\/+M4S4Pط^VsJO:KH<4OYskêdӑ3OPJg8 IA+0uix=^*e!PCmDФ9&ʰITc|/3+~gD3~}6ƀ }d-7K15_Ri:1z*oFx>\?ڱxEء{|BD(WPLS`g}QNH52$AOs")[|_ngq3%لWD JцbiTɇDezOӋWa"Ƀ 6~H,f](nvG¸+|hW"qR({|us’Z4U $0; :}m0pJx (ߠɽn*{btrο";MA┟kyf*aRT XOUS~[Mwsr xGp}N((1}a\8ټcBӁ9v&*X.14қ`8L˲Hlǚ,y$UR?ԋڧ[t3NaX2l(Ø%—m|fs/$>m\$;Lb쫯&b5l?{1h29Aq~W6CG:]D`N";Z_(4[Tې_"LP/K,g&,x@OP{ <{KPT~+1L ʑ!&U!U &ߗg]@-b<4/3d`W:3B|`1ձ}>0ꗧ~&ƌz v5nzo[s];tV應Ѻ1hor2AR48Xb҈lW1!{膩$O Y|g9 +U6Gc\} sN~v/$Le^ߪN- CŔD1i\vPg z4b \]NS=MwIKZKv $M1bPZdztlEگϲEF'5U}Ѻi'oM47Az.' zo%9^I+nJz}%h7MߵHvˋgTě#+|Uѥo֞ [ͭeY 2B%slYSߝИ"0b'04̂u9`39F' ,/. c.0PuhOJRԧۯ+=s ",nbYdcnt{%?d)Z(ް +›mbMXyQ|bLmTTKgpiĖH _?PG6R'iGBzoCvU oK\]%.)u :x?J-~ǡYKJRIZT=w]tΤs;~BspmH)RRe'Q0~dr&Cjz^gڟ?d,_'yh~ne^5!o9yIO@:vmuRYZ);&&\H^σMɛc}G EݖPOiimԟ;%kлf J#P٦OOğ\B61&zZс*m|wtiԂ0BXTŲV歱3TF}vx9̷BlFL,ZG7"[!ªn\lc,kgْdMPmv Z(>hO)"I܈vQsͫ1^c8"GwǮJpRxo֟۞Z- oE?KvTu]@;gJ\ꑙ.#ԻnY'P_9AڲA !9$cqτbK@3Uk]@c+缶 W1CbXTu~ fPR afT~R:bi} RVgtAط98Jo:QvwUV45{ h/V *ٍjO2?>U9t/-&P0\3`Ǎc Y09Hwj`f񀫪b[L_ DKȸA4}ay)?1ϴ?L:sK.4՟Z2P#A WoEPJ9b]mal˝H3 .]ktcKc׬Iy풌l0к68^HWH|`v3 vA$;DJ4@Q4~mxcM;|A Ӟdzd|y&Yܮ\+HFO᭧p| c$kpo2g  M$ӶRϓOWu`^zIA[-sw ЃnA>Qծa朦::S U+.{NePa@/P" /H<[:ފJ^^>'q3Ny ?Ir\+eE^59p)[=kqYglMP9wP-ǁui虱p$D>O$j۩ M jZݯ/,2VlGߺ7.S@.*pi?ȸbv۲uo Ls~Z|7Y#ڹ&z-S?K1ITUEìIZ+S.|fYkLO;_vTv'͟(,%kP])fSh=T%@cZ6ʳhRr\B= xX)Q`o-C,qCL`8I"yDyU\J_tD{Q]54w6rNn_MYnߙ-01l0>0!0 +4}s?2!3M:VH5ֵhttpcore5/src/test/resources/ca.p120100664 0000000 0000000 00000005147 14245617503 016231 0ustar000000000 0000000 0 c0  *H   0 0a *H RN0J0F *H  00) *H  0./(63tOޠnQP|E5Ge{EvRQU$?ܸ)]w- v&9cz-8A K'}JQs/-OtB]._YW(f1Ir8]"XJ>]\HMkVܑVǣu9!h,u-~#O~8gc/lNJ7,j ڷRaүpQ~n |}a**GV;c>94EO`ӕo(tF0]Arv"ɽ޲3)!_I|2ΝsA-Y=dhjX\H`\XmXi]-aac?z|uRD5`m"TiC~c§J+UpL sM؂LPZ.J#.!nn2UW:p`)_:8;4ξQk&jcp< 8n~cFMv_@?|]8|8HH @W%7Ff/3 %_^09rz xr򭈻;G2UNWEH-]m%*D'hc_ 5wIm`e6[ҹOcaa~j;5nCq(̵`ZR.9Fs9~ '΢en& f(\F],ylT(-hF[of(%=I 4@ݪ7.oW!ror8X$>_$/cvWCO&}pdܲh㐸@X]@169*ݚ; 7Myv3y^νj8F{$^}e#YE]̟C㟔Ch.Tv Оv}: Iʍt ˊmEiYF ZcExA]$Ȱ;4fs9%Fi(ˏjXg3dk f&zݓ:.3qZuƩsM$E ѪFBI)Jc4Z>Fnd#IV  * 'E2P180 *H  1ca0! *H  1Time 15708093446860 *H 00 *H 0) *H  01㸪e](XPH>&&Uz<+ Yi DS| ,Qu&Twv&Bs`8r`ǧf \>|pWhXeJI 2f$ cta'9Ϊ9Bnh'6K ̸>qXF cuüYRH``^.-Jk{En:h Ckcfe ch ZǧS~a0JҜulA[78?|Qȍl0 m!n<~"4HXb S+9U-6ƥnr|Ӭ#5/$y %b(6iȑO!vGt34m٭5U y wX1]4+ctS28=bP}=Ĕ/8i7[{sCԢ\')`j^W<}MVTX8f09pC`"7)-MF\_¸}!KmrD =9?3Up( Qqw^9-wb6g Auc[flm 3}h ^8I1SoA*?Ys0,CNGҰC#7JIdy,v߆@YMժ_ T0Їu3 ׯT4e|zC h0ϲ\C7[ 猔@хfb-FlVܻMqBw..\LM[iZI-Ɲڑ7t+"u٠PK `ۉ1 Ů0!0 +X'_e`ڙh6&GjI<<3b~p[httpcore5/src/test/java/0040775 0000000 0000000 00000000000 14245617503 014225 5ustar000000000 0000000 httpcore5/src/test/java/org/0040775 0000000 0000000 00000000000 14245617503 015014 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/0040775 0000000 0000000 00000000000 14245617503 016235 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/0040775 0000000 0000000 00000000000 14245617503 016627 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/0040775 0000000 0000000 00000000000 14403631147 017640 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/net/0040775 0000000 0000000 00000000000 14435411677 020437 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/net/TestPercentCodec.java0100664 0000000 0000000 00000005346 14403631147 024474 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.charset.StandardCharsets; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; /** * Unit tests for {@link PercentCodec}. */ public class TestPercentCodec { @Test public void testCoding() { final StringBuilder buf = new StringBuilder(); PercentCodec.encode(buf, "blah!", StandardCharsets.UTF_8); PercentCodec.encode(buf, " ~ ", StandardCharsets.UTF_8); PercentCodec.encode(buf, "huh?", StandardCharsets.UTF_8); assertThat(buf.toString(), CoreMatchers.equalTo("blah%21%20~%20huh%3F")); } @Test public void testDecoding() { assertThat(PercentCodec.decode("blah%21%20~%20huh%3F", StandardCharsets.UTF_8), CoreMatchers.equalTo("blah! ~ huh?")); assertThat(PercentCodec.decode("blah%21+~%20huh%3F", StandardCharsets.UTF_8), CoreMatchers.equalTo("blah!+~ huh?")); assertThat(PercentCodec.decode("blah%21+~%20huh%3F", StandardCharsets.UTF_8, true), CoreMatchers.equalTo("blah! ~ huh?")); } @Test public void testDecodingPartialContent() { assertThat(PercentCodec.decode("blah%21%20%", StandardCharsets.UTF_8), CoreMatchers.equalTo("blah! %")); assertThat(PercentCodec.decode("blah%21%20%a", StandardCharsets.UTF_8), CoreMatchers.equalTo("blah! %a")); assertThat(PercentCodec.decode("blah%21%20%wa", StandardCharsets.UTF_8), CoreMatchers.equalTo("blah! %wa")); } } httpcore5/src/test/java/org/apache/hc/core5/net/TestWWWFormCodec.java0100664 0000000 0000000 00000014365 14403631147 024405 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.NameValuePairListMatcher; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; public class TestWWWFormCodec { private static final String CH_HELLO = "\u0047\u0072\u00FC\u0065\u007A\u0069\u005F\u007A\u00E4\u006D\u00E4"; private static final String RU_HELLO = "\u0412\u0441\u0435\u043C\u005F\u043F\u0440\u0438\u0432\u0435\u0442"; private static List parse(final String params) { return WWWFormCodec.parse(params, StandardCharsets.UTF_8); } @Test public void testParse() throws Exception { assertThat(parse(""), NameValuePairListMatcher.isEmpty()); assertThat(parse("Name0"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name0", null))); assertThat(parse("Name1=Value1"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name1", "Value1"))); assertThat(parse("Name2="), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name2", ""))); assertThat(parse(" Name3 "), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name3", null))); assertThat(parse("Name4=Value%204%21"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value 4!"))); assertThat(parse("Name4=Value%2B4%21"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value+4!"))); assertThat(parse("Name4=Value%204%21%20%214"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value 4! !4"))); assertThat(parse("Name5=aaa&Name6=bbb"), NameValuePairListMatcher.equalsTo( new BasicNameValuePair("Name5", "aaa"), new BasicNameValuePair("Name6", "bbb"))); assertThat(parse("Name7=aaa&Name7=b%2Cb&Name7=ccc"), NameValuePairListMatcher.equalsTo( new BasicNameValuePair("Name7", "aaa"), new BasicNameValuePair("Name7", "b,b"), new BasicNameValuePair("Name7", "ccc"))); assertThat(parse("Name8=xx%2C%20%20yy%20%20%2Czz"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name8", "xx, yy ,zz"))); assertThat(parse("price=10%20%E2%82%AC"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("price", "10 \u20AC"))); assertThat(parse("a=b\"c&d=e"), NameValuePairListMatcher.equalsTo( new BasicNameValuePair("a", "b\"c"), new BasicNameValuePair("d", "e"))); assertThat(parse("russian=" + PercentCodec.encode(RU_HELLO, StandardCharsets.UTF_8) + "&swiss=" + PercentCodec.encode(CH_HELLO, StandardCharsets.UTF_8)), NameValuePairListMatcher.equalsTo( new BasicNameValuePair("russian", RU_HELLO), new BasicNameValuePair("swiss", CH_HELLO))); } private static String format(final NameValuePair... nvps) { return WWWFormCodec.format(Arrays.asList(nvps), StandardCharsets.UTF_8); } @Test public void testFormat() throws Exception { assertThat(format(new BasicNameValuePair("Name0", null)), CoreMatchers.equalTo("Name0")); assertThat(format(new BasicNameValuePair("Name1", "Value1")), CoreMatchers.equalTo("Name1=Value1")); assertThat(format(new BasicNameValuePair("Name2", "")), CoreMatchers.equalTo("Name2=")); assertThat(format(new BasicNameValuePair("Name4", "Value 4&")), CoreMatchers.equalTo("Name4=Value+4%26")); assertThat(format(new BasicNameValuePair("Name4", "Value+4&")), CoreMatchers.equalTo("Name4=Value%2B4%26")); assertThat(format(new BasicNameValuePair("Name4", "Value 4& =4")), CoreMatchers.equalTo("Name4=Value+4%26+%3D4")); assertThat(format( new BasicNameValuePair("Name5", "aaa"), new BasicNameValuePair("Name6", "bbb")), CoreMatchers.equalTo("Name5=aaa&Name6=bbb")); assertThat(format( new BasicNameValuePair("Name7", "aaa"), new BasicNameValuePair("Name7", "b,b"), new BasicNameValuePair("Name7", "ccc") ), CoreMatchers.equalTo("Name7=aaa&Name7=b%2Cb&Name7=ccc")); assertThat(format(new BasicNameValuePair("Name8", "xx, yy ,zz")), CoreMatchers.equalTo("Name8=xx%2C++yy++%2Czz")); assertThat(format( new BasicNameValuePair("russian", RU_HELLO), new BasicNameValuePair("swiss", CH_HELLO)), CoreMatchers.equalTo("russian=" + PercentCodec.encode(RU_HELLO, StandardCharsets.UTF_8) + "&swiss=" + PercentCodec.encode(CH_HELLO, StandardCharsets.UTF_8))); } } httpcore5/src/test/java/org/apache/hc/core5/net/TestURIAuthority.java0100664 0000000 0000000 00000026065 14403631147 024507 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import static org.hamcrest.MatcherAssert.assertThat; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.URISyntaxException; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link URIAuthority}. * */ public class TestURIAuthority { @Test public void testConstructor() { final URIAuthority host1 = new URIAuthority("somehost"); Assertions.assertEquals("somehost", host1.getHostName()); Assertions.assertEquals(-1, host1.getPort()); final URIAuthority host2 = new URIAuthority("somehost", 8080); Assertions.assertEquals("somehost", host2.getHostName()); Assertions.assertEquals(8080, host2.getPort()); final URIAuthority host3 = new URIAuthority("somehost", -1); Assertions.assertEquals("somehost", host3.getHostName()); Assertions.assertEquals(-1, host3.getPort()); } @Test public void testHashCode() throws Exception { final URIAuthority host1 = new URIAuthority("somehost", 8080); final URIAuthority host2 = new URIAuthority("somehost", 80); final URIAuthority host3 = new URIAuthority("someotherhost", 8080); final URIAuthority host4 = new URIAuthority("somehost", 80); final URIAuthority host5 = new URIAuthority("SomeHost", 80); final URIAuthority host6 = new URIAuthority("user", "SomeHost", 80); final URIAuthority host7 = new URIAuthority("user", "somehost", 80); Assertions.assertEquals(host1.hashCode(), host1.hashCode()); Assertions.assertTrue(host1.hashCode() != host2.hashCode()); Assertions.assertTrue(host1.hashCode() != host3.hashCode()); Assertions.assertEquals(host2.hashCode(), host4.hashCode()); Assertions.assertEquals(host2.hashCode(), host5.hashCode()); Assertions.assertTrue(host5.hashCode() != host6.hashCode()); Assertions.assertEquals(host6.hashCode(), host7.hashCode()); } @Test public void testEquals() throws Exception { final URIAuthority host1 = new URIAuthority("somehost", 8080); final URIAuthority host2 = new URIAuthority("somehost", 80); final URIAuthority host3 = new URIAuthority("someotherhost", 8080); final URIAuthority host4 = new URIAuthority("somehost", 80); final URIAuthority host5 = new URIAuthority("SomeHost", 80); final URIAuthority host6 = new URIAuthority("user", "SomeHost", 80); final URIAuthority host7 = new URIAuthority("user", "somehost", 80); Assertions.assertEquals(host1, host1); Assertions.assertNotEquals(host1, host2); Assertions.assertNotEquals(host1, host3); Assertions.assertEquals(host2, host4); Assertions.assertEquals(host2, host5); Assertions.assertNotEquals(host5, host6); Assertions.assertEquals(host6, host7); } @Test public void testToString() throws Exception { final URIAuthority host1 = new URIAuthority("somehost"); Assertions.assertEquals("somehost", host1.toString()); final URIAuthority host2 = new URIAuthority("somehost", -1); Assertions.assertEquals("somehost", host2.toString()); final URIAuthority host3 = new URIAuthority("somehost", 8888); Assertions.assertEquals("somehost:8888", host3.toString()); } @Test public void testSerialization() throws Exception { final URIAuthority orig = new URIAuthority("somehost", 8080); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer); outStream.writeObject(orig); outStream.close(); final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final URIAuthority clone = (URIAuthority) inStream.readObject(); Assertions.assertEquals(orig, clone); } @Test public void testParse() throws Exception { assertThat(URIAuthority.parse("somehost"), CoreMatchers.equalTo(new URIAuthority("somehost", -1))); assertThat(URIAuthority.parse("somehost/blah"), CoreMatchers.equalTo(new URIAuthority("somehost", -1))); assertThat(URIAuthority.parse("somehost?blah"), CoreMatchers.equalTo(new URIAuthority("somehost", -1))); assertThat(URIAuthority.parse("somehost#blah"), CoreMatchers.equalTo(new URIAuthority("somehost", -1))); assertThat(URIAuthority.parse("aaaa@:8080"), CoreMatchers.equalTo(new URIAuthority("aaaa", "", 8080))); assertThat(URIAuthority.parse("@:"), CoreMatchers.equalTo(new URIAuthority(null, "", -1))); assertThat(URIAuthority.parse("somehost:8080"), CoreMatchers.equalTo(new URIAuthority("somehost", 8080))); assertThat(URIAuthority.parse("somehost:8080/blah"), CoreMatchers.equalTo(new URIAuthority("somehost", 8080))); assertThat(URIAuthority.parse("somehost:8080?blah"), CoreMatchers.equalTo(new URIAuthority("somehost", 8080))); assertThat(URIAuthority.parse("somehost:8080#blah"), CoreMatchers.equalTo(new URIAuthority("somehost", 8080))); assertThat(URIAuthority.parse("somehost:008080"), CoreMatchers.equalTo(new URIAuthority("somehost", 8080))); Assertions.assertThrows(URISyntaxException.class, () -> URIAuthority.create("somehost:aaaaa")); Assertions.assertThrows(URISyntaxException.class, () -> URIAuthority.create("somehost:90ab")); assertThat(URIAuthority.parse("someuser@somehost"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", -1))); assertThat(URIAuthority.parse("someuser@somehost/blah"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", -1))); assertThat(URIAuthority.parse("someuser@somehost?blah"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", -1))); assertThat(URIAuthority.parse("someuser@somehost#blah"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", -1))); assertThat(URIAuthority.parse("someuser@somehost:"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", -1))); assertThat(URIAuthority.parse("someuser@somehost:/blah"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", -1))); assertThat(URIAuthority.parse("someuser@somehost:?blah"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", -1))); assertThat(URIAuthority.parse("someuser@somehost:#blah"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", -1))); assertThat(URIAuthority.parse("someuser@somehost:8080"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", 8080))); assertThat(URIAuthority.parse("someuser@somehost:8080/blah"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", 8080))); assertThat(URIAuthority.parse("someuser@somehost:8080?blah"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", 8080))); assertThat(URIAuthority.parse("someuser@somehost:8080#blah"), CoreMatchers.equalTo(new URIAuthority("someuser", "somehost", 8080))); assertThat(URIAuthority.parse("@somehost:8080"), CoreMatchers.equalTo(new URIAuthority("somehost", 8080))); assertThat(URIAuthority.parse("test:test@localhost:38339"), CoreMatchers.equalTo(new URIAuthority("test:test", "localhost", 38339))); Assertions.assertThrows(URISyntaxException.class, () -> URIAuthority.create("blah@goggle.com:80@google.com/")); } @Test public void testCreateFromString() throws Exception { Assertions.assertEquals(new URIAuthority("somehost", 8080), URIAuthority.create("somehost:8080")); Assertions.assertEquals(new URIAuthority("SomeHost", 8080), URIAuthority.create("SomeHost:8080")); Assertions.assertEquals(new URIAuthority("somehost", 1234), URIAuthority.create("somehost:1234")); Assertions.assertEquals(new URIAuthority("somehost", -1), URIAuthority.create("somehost")); Assertions.assertEquals(new URIAuthority("user", "somehost", -1), URIAuthority.create("user@somehost")); Assertions.assertThrows(URISyntaxException.class, () -> URIAuthority.create(" host")); Assertions.assertThrows(URISyntaxException.class, () -> URIAuthority.create("host ")); Assertions.assertThrows(URISyntaxException.class, () -> URIAuthority.create("host :8080")); Assertions.assertThrows(URISyntaxException.class, () -> URIAuthority.create("user @ host:8080")); } @Test public void testCreateFromIPv6String() throws Exception { Assertions.assertEquals(new URIAuthority("::1", 8080), URIAuthority.create("[::1]:8080")); Assertions.assertEquals(new URIAuthority("::1", -1), URIAuthority.create("[::1]")); Assertions.assertThrows(URISyntaxException.class, () -> URIAuthority.create("::1")); Assertions.assertThrows(URISyntaxException.class, () -> URIAuthority.create("[::1")); Assertions.assertThrows(URISyntaxException.class, () -> URIAuthority.create("[a]:8080")); } @Test public void testIpv6HostToString() { Assertions.assertEquals("[::1]:80", new URIAuthority("::1", 80).toString()); Assertions.assertEquals("user@[::1]:80", new URIAuthority("user", "::1", 80).toString()); Assertions.assertEquals("[::1]", new URIAuthority("::1", -1).toString()); Assertions.assertEquals("user@[::1]", new URIAuthority("user", "::1", -1).toString()); } } httpcore5/src/test/java/org/apache/hc/core5/net/TestInetAddressUtils.java0100664 0000000 0000000 00000025636 14435411677 025401 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for InetAddressUtils. */ public class TestInetAddressUtils { @Test public void testValidIPv4Address() { Assertions.assertTrue(InetAddressUtils.isIPv4Address("127.0.0.1")); Assertions.assertTrue(InetAddressUtils.isIPv4Address("192.168.0.0")); Assertions.assertTrue(InetAddressUtils.isIPv4Address("255.255.255.255")); } @Test public void testInvalidIPv4Address() { Assertions.assertFalse(InetAddressUtils.isIPv4Address(" 127.0.0.1 ")); // Blanks not allowed Assertions.assertFalse(InetAddressUtils.isIPv4Address("g.ar.ba.ge")); Assertions.assertFalse(InetAddressUtils.isIPv4Address("192.168.0")); Assertions.assertFalse(InetAddressUtils.isIPv4Address("256.255.255.255")); Assertions.assertFalse(InetAddressUtils.isIPv4Address("0.168.0.0")); //IP address that starts with zero not allowed } @Test public void testValidIPv6Address() { Assertions.assertTrue(InetAddressUtils.isIPv6StdAddress("2001:0db8:0000:0000:0000:0000:1428:57ab")); Assertions.assertTrue(InetAddressUtils.isIPv6StdAddress("2001:db8:0:0:0:0:1428:57ab")); Assertions.assertTrue(InetAddressUtils.isIPv6StdAddress("0:0:0:0:0:0:0:0")); Assertions.assertTrue(InetAddressUtils.isIPv6StdAddress("0:0:0:0:0:0:0:1")); Assertions.assertTrue(InetAddressUtils.isIPv6HexCompressedAddress("2001:0db8:0:0::1428:57ab")); Assertions.assertTrue(InetAddressUtils.isIPv6HexCompressedAddress("2001:0db8::1428:57ab")); Assertions.assertTrue(InetAddressUtils.isIPv6HexCompressedAddress("2001:db8::1428:57ab")); Assertions.assertTrue(InetAddressUtils.isIPv6HexCompressedAddress("::1")); Assertions.assertTrue(InetAddressUtils.isIPv6HexCompressedAddress("::")); // http://tools.ietf.org/html/rfc4291#section-2.2 Assertions.assertTrue(InetAddressUtils.isIPv6Address("2001:0db8:0000:0000:0000:0000:1428:57ab")); Assertions.assertTrue(InetAddressUtils.isIPv6Address("2001:db8:0:0:0:0:1428:57ab")); Assertions.assertTrue(InetAddressUtils.isIPv6Address("0:0:0:0:0:0:0:0")); Assertions.assertTrue(InetAddressUtils.isIPv6Address("0:0:0:0:0:0:0:1")); Assertions.assertTrue(InetAddressUtils.isIPv6Address("2001:0db8:0:0::1428:57ab")); Assertions.assertTrue(InetAddressUtils.isIPv6Address("2001:0db8::1428:57ab")); Assertions.assertTrue(InetAddressUtils.isIPv6Address("2001:db8::1428:57ab")); Assertions.assertTrue(InetAddressUtils.isIPv6Address("::1")); Assertions.assertTrue(InetAddressUtils.isIPv6Address("::")); // http://tools.ietf.org/html/rfc4291#section-2.2 } @Test public void testInvalidIPv6Address() { Assertions.assertFalse(InetAddressUtils.isIPv6Address("2001:0db8:0000:garb:age0:0000:1428:57ab")); Assertions.assertFalse(InetAddressUtils.isIPv6Address("2001:0gb8:0000:0000:0000:0000:1428:57ab")); Assertions.assertFalse(InetAddressUtils.isIPv6StdAddress("0:0:0:0:0:0:0:0:0")); // Too many Assertions.assertFalse(InetAddressUtils.isIPv6StdAddress("0:0:0:0:0:0:0")); // Too few Assertions.assertFalse(InetAddressUtils.isIPv6HexCompressedAddress(":1")); Assertions.assertFalse(InetAddressUtils.isIPv6Address(":1")); Assertions.assertFalse(InetAddressUtils.isIPv6Address("2001:0db8::0000::57ab")); // Cannot have two contractions Assertions.assertFalse(InetAddressUtils.isIPv6HexCompressedAddress("1:2:3:4:5:6:7::9")); // too many fields before :: Assertions.assertFalse(InetAddressUtils.isIPv6HexCompressedAddress("1::3:4:5:6:7:8:9")); // too many fields after :: Assertions.assertFalse(InetAddressUtils.isIPv6HexCompressedAddress("::3:4:5:6:7:8:9")); // too many fields after :: Assertions.assertFalse(InetAddressUtils.isIPv6Address("")); // empty } @Test public void testValidIPv6BracketAddress() { Assertions.assertTrue(InetAddressUtils.isIPv6URLBracketedAddress("[2001:0db8:0000:0000:0000:0000:1428:57ab]")); Assertions.assertTrue(InetAddressUtils.isIPv6URLBracketedAddress("[2001:db8:0:0:0:0:1428:57ab]")); Assertions.assertTrue(InetAddressUtils.isIPv6URLBracketedAddress("[0:0:0:0:0:0:0:0]")); Assertions.assertTrue(InetAddressUtils.isIPv6URLBracketedAddress("[0:0:0:0:0:0:0:1]")); Assertions.assertTrue(InetAddressUtils.isIPv6URLBracketedAddress("[2001:0db8:0:0::1428:57ab]")); Assertions.assertTrue(InetAddressUtils.isIPv6URLBracketedAddress("[2001:0db8::1428:57ab]")); Assertions.assertTrue(InetAddressUtils.isIPv6URLBracketedAddress("[2001:db8::1428:57ab]")); Assertions.assertTrue(InetAddressUtils.isIPv6URLBracketedAddress("[::1]")); // http://tools.ietf.org/html/rfc4291#section-2.2 Assertions.assertTrue(InetAddressUtils.isIPv6URLBracketedAddress("[::]")); } @Test public void testInvalidIPv6BracketAddress() { Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("2001:0db8:0000:garb:age0:0000:1428:57ab")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("[2001:0db8:0000:garb:age0:0000:1428:57ab]")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("2001:0gb8:0000:0000:0000:0000:1428:57ab")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("[2001:0gb8:0000:0000:0000:0000:1428:57ab]")); // Too many Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("0:0:0:0:0:0:0:0:0")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("[0:0:0:0:0:0:0:0:0]")); // Too few Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("0:0:0:0:0:0:0")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("[0:0:0:0:0:0:0]")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress(":1")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("[:1]")); // Cannot have two contractions Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("2001:0db8::0000::57ab")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("[2001:0db8::0000::57ab]")); // too many fields before :: Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("1:2:3:4:5:6:7::9")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("[1:2:3:4:5:6:7::9]")); // too many fields after :: Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("1::3:4:5:6:7:8:9")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("[1::3:4:5:6:7:8:9]")); // too many fields after :: Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("::3:4:5:6:7:8:9")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("[::3:4:5:6:7:8:9]")); // empty Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("[]")); // missing brackets Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("::")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("::1")); Assertions.assertFalse(InetAddressUtils.isIPv6URLBracketedAddress("2001:db8::1428:57ab")); } @Test // Test HTTPCLIENT-1319 public void testInvalidIPv6AddressIncorrectGroupCount() { Assertions.assertFalse(InetAddressUtils.isIPv6HexCompressedAddress("1:2::4:5:6:7:8:9")); // too many fields in total Assertions.assertFalse(InetAddressUtils.isIPv6HexCompressedAddress("1:2:3:4:5:6::8:9")); // too many fields in total } @Test public void testHasValidIPv6ColonCount() { Assertions.assertFalse(InetAddressUtils.hasValidIPv6ColonCount("")); Assertions.assertFalse(InetAddressUtils.hasValidIPv6ColonCount(":")); Assertions.assertFalse(InetAddressUtils.hasValidIPv6ColonCount("127.0.0.1")); Assertions.assertFalse(InetAddressUtils.hasValidIPv6ColonCount(":0")); Assertions.assertFalse(InetAddressUtils.hasValidIPv6ColonCount("0:")); Assertions.assertFalse(InetAddressUtils.hasValidIPv6ColonCount("1:2:3:4:5:6:7:8:")); Assertions.assertFalse(InetAddressUtils.hasValidIPv6ColonCount("1:2:3:4:5:6:7:8:9")); Assertions.assertTrue(InetAddressUtils.hasValidIPv6ColonCount("2001:0db8:0000:0000:0000:0000:1428:57ab")); Assertions.assertTrue(InetAddressUtils.hasValidIPv6ColonCount("2001:db8:0:0:0:0:1428:57ab")); Assertions.assertTrue(InetAddressUtils.hasValidIPv6ColonCount("0:0:0:0:0:0:0:0")); Assertions.assertTrue(InetAddressUtils.hasValidIPv6ColonCount("0:0:0:0:0:0:0:1")); Assertions.assertTrue(InetAddressUtils.hasValidIPv6ColonCount("2001:0db8:0:0::1428:57ab")); Assertions.assertTrue(InetAddressUtils.hasValidIPv6ColonCount("2001:0db8::1428:57ab")); Assertions.assertTrue(InetAddressUtils.hasValidIPv6ColonCount("2001:db8::1428:57ab")); Assertions.assertTrue(InetAddressUtils.hasValidIPv6ColonCount("::1")); Assertions.assertTrue(InetAddressUtils.hasValidIPv6ColonCount("::")); // http://tools.ietf.org/html/rfc4291#section-2.2 } @Test public void testValidIPv4MappedIPv6Address() { Assertions.assertTrue(InetAddressUtils.isIPv4MappedIPv64Address("::FFFF:1.2.3.4")); Assertions.assertTrue(InetAddressUtils.isIPv4MappedIPv64Address("::ffff:255.255.255.255")); } @Test public void testInValidIPv4MappedIPv6Address() { Assertions.assertFalse(InetAddressUtils.isIPv4MappedIPv64Address("2001:0db8:0000:0000:0000:0000:1428:57ab")); Assertions.assertFalse(InetAddressUtils.isIPv4MappedIPv64Address("::ffff:1:2:3:4")); } } httpcore5/src/test/java/org/apache/hc/core5/net/TestURIBuilder.java0100664 0000000 0000000 00000124742 14435411677 024117 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import static org.hamcrest.MatcherAssert.assertThat; import java.net.InetAddress; import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.NameValuePairListMatcher; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestURIBuilder { private static final String CH_HELLO = "\u0047\u0072\u00FC\u0065\u007A\u0069\u005F\u007A\u00E4\u006D\u00E4"; private static final String RU_HELLO = "\u0412\u0441\u0435\u043C\u005F\u043F\u0440\u0438\u0432\u0435\u0442"; static List parsePath(final CharSequence s) { return URIBuilder.parsePath(s, null); } @Test public void testParseSegments() throws Exception { assertThat(parsePath("/this/that"), CoreMatchers.equalTo(Arrays.asList("this", "that"))); assertThat(parsePath("this/that"), CoreMatchers.equalTo(Arrays.asList("this", "that"))); assertThat(parsePath("this//that"), CoreMatchers.equalTo(Arrays.asList("this", "", "that"))); assertThat(parsePath("this//that/"), CoreMatchers.equalTo(Arrays.asList("this", "", "that", ""))); assertThat(parsePath("this//that/%2fthis%20and%20that"), CoreMatchers.equalTo(Arrays.asList("this", "", "that", "/this and that"))); assertThat(parsePath("this///that//"), CoreMatchers.equalTo(Arrays.asList("this", "", "", "that", "", ""))); assertThat(parsePath("/"), CoreMatchers.equalTo(Collections.singletonList(""))); assertThat(parsePath(""), CoreMatchers.equalTo(Collections.emptyList())); } static String formatPath(final String... pathSegments) { final StringBuilder buf = new StringBuilder(); URIBuilder.formatPath(buf, Arrays.asList(pathSegments), false, null); return buf.toString(); } @Test public void testFormatSegments() throws Exception { assertThat(formatPath("this", "that"), CoreMatchers.equalTo("/this/that")); assertThat(formatPath("this", "", "that"), CoreMatchers.equalTo("/this//that")); assertThat(formatPath("this", "", "that", "/this and that"), CoreMatchers.equalTo("/this//that/%2Fthis%20and%20that")); assertThat(formatPath("this", "", "", "that", "", ""), CoreMatchers.equalTo("/this///that//")); assertThat(formatPath(""), CoreMatchers.equalTo("/")); assertThat(formatPath(), CoreMatchers.equalTo("")); } static List parseQuery(final CharSequence s) { return URIBuilder.parseQuery(s, null, false); } @Test public void testParseQuery() throws Exception { assertThat(parseQuery(""), NameValuePairListMatcher.isEmpty()); assertThat(parseQuery("Name0"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name0", null))); assertThat(parseQuery("Name1=Value1"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name1", "Value1"))); assertThat(parseQuery("Name2="), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name2", ""))); assertThat(parseQuery(" Name3 "), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name3", null))); assertThat(parseQuery("Name4=Value%204%21"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value 4!"))); assertThat(parseQuery("Name4=Value%2B4%21"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value+4!"))); assertThat(parseQuery("Name4=Value%204%21%20%214"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value 4! !4"))); assertThat(parseQuery("Name5=aaa&Name6=bbb"), NameValuePairListMatcher.equalsTo( new BasicNameValuePair("Name5", "aaa"), new BasicNameValuePair("Name6", "bbb"))); assertThat(parseQuery("Name7=aaa&Name7=b%2Cb&Name7=ccc"), NameValuePairListMatcher.equalsTo( new BasicNameValuePair("Name7", "aaa"), new BasicNameValuePair("Name7", "b,b"), new BasicNameValuePair("Name7", "ccc"))); assertThat(parseQuery("Name8=xx%2C%20%20yy%20%20%2Czz"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name8", "xx, yy ,zz"))); assertThat(parseQuery("price=10%20%E2%82%AC"), NameValuePairListMatcher.equalsTo(new BasicNameValuePair("price", "10 \u20AC"))); assertThat(parseQuery("a=b\"c&d=e"), NameValuePairListMatcher.equalsTo( new BasicNameValuePair("a", "b\"c"), new BasicNameValuePair("d", "e"))); assertThat(parseQuery("russian=" + PercentCodec.encode(RU_HELLO, StandardCharsets.UTF_8) + "&swiss=" + PercentCodec.encode(CH_HELLO, StandardCharsets.UTF_8)), NameValuePairListMatcher.equalsTo( new BasicNameValuePair("russian", RU_HELLO), new BasicNameValuePair("swiss", CH_HELLO))); } static String formatQuery(final NameValuePair... params) { final StringBuilder buf = new StringBuilder(); URIBuilder.formatQuery(buf, Arrays.asList(params), null, false); return buf.toString(); } @Test public void testFormatQuery() throws Exception { assertThat(formatQuery(new BasicNameValuePair("Name0", null)), CoreMatchers.equalTo("Name0")); assertThat(formatQuery(new BasicNameValuePair("Name1", "Value1")), CoreMatchers.equalTo("Name1=Value1")); assertThat(formatQuery(new BasicNameValuePair("Name2", "")), CoreMatchers.equalTo("Name2=")); assertThat(formatQuery(new BasicNameValuePair("Name4", "Value 4&")), CoreMatchers.equalTo("Name4=Value%204%26")); assertThat(formatQuery(new BasicNameValuePair("Name4", "Value+4&")), CoreMatchers.equalTo("Name4=Value%2B4%26")); assertThat(formatQuery(new BasicNameValuePair("Name4", "Value 4& =4")), CoreMatchers.equalTo("Name4=Value%204%26%20%3D4")); assertThat(formatQuery( new BasicNameValuePair("Name5", "aaa"), new BasicNameValuePair("Name6", "bbb")), CoreMatchers.equalTo("Name5=aaa&Name6=bbb")); assertThat(formatQuery( new BasicNameValuePair("Name7", "aaa"), new BasicNameValuePair("Name7", "b,b"), new BasicNameValuePair("Name7", "ccc") ), CoreMatchers.equalTo("Name7=aaa&Name7=b%2Cb&Name7=ccc")); assertThat(formatQuery(new BasicNameValuePair("Name8", "xx, yy ,zz")), CoreMatchers.equalTo("Name8=xx%2C%20%20yy%20%20%2Czz")); assertThat(formatQuery( new BasicNameValuePair("russian", RU_HELLO), new BasicNameValuePair("swiss", CH_HELLO)), CoreMatchers.equalTo("russian=" + PercentCodec.encode(RU_HELLO, StandardCharsets.UTF_8) + "&swiss=" + PercentCodec.encode(CH_HELLO, StandardCharsets.UTF_8))); } @Test public void testHierarchicalUri() throws Exception { final URI uri = new URI("http", "stuff", "localhost", 80, "/some stuff", "param=stuff", "fragment"); final URIBuilder uribuilder = new URIBuilder(uri); final URI result = uribuilder.build(); Assertions.assertEquals(new URI("http://stuff@localhost:80/some%20stuff?param=stuff#fragment"), result); } @Test public void testMutationToRelativeUri() throws Exception { final URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment"); final URIBuilder uribuilder = new URIBuilder(uri).setHost((String) null); final URI result = uribuilder.build(); Assertions.assertEquals(new URI("http:///stuff?param=stuff#fragment"), result); } @Test public void testMutationRemoveFragment() throws Exception { final URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment"); final URI result = new URIBuilder(uri).setFragment(null).build(); Assertions.assertEquals(new URI("http://stuff@localhost:80/stuff?param=stuff"), result); } @Test public void testMutationRemoveUserInfo() throws Exception { final URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment"); final URI result = new URIBuilder(uri).setUserInfo(null).build(); Assertions.assertEquals(new URI("http://localhost:80/stuff?param=stuff#fragment"), result); } @Test public void testMutationRemovePort() throws Exception { final URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment"); final URI result = new URIBuilder(uri).setPort(-1).build(); Assertions.assertEquals(new URI("http://stuff@localhost/stuff?param=stuff#fragment"), result); } @Test public void testOpaqueUri() throws Exception { final URI uri = new URI("stuff", "some-stuff", "fragment"); final URIBuilder uribuilder = new URIBuilder(uri); final URI result = uribuilder.build(); Assertions.assertEquals(uri, result); } @Test public void testOpaqueUriMutation() throws Exception { final URI uri = new URI("stuff", "some-stuff", "fragment"); final URIBuilder uribuilder = new URIBuilder(uri).setCustomQuery("param1¶m2=stuff").setFragment(null); Assertions.assertEquals(new URI("stuff:?param1¶m2=stuff"), uribuilder.build()); } @Test public void testHierarchicalUriMutation() throws Exception { final URIBuilder uribuilder = new URIBuilder("/").setScheme("http").setHost("localhost").setPort(80).setPath("/stuff"); Assertions.assertEquals(new URI("http://localhost:80/stuff"), uribuilder.build()); } @Test public void testLocalhost() throws Exception { // Check that the URI generated by URI builder agrees with that generated by using URI directly final String scheme="https"; final InetAddress host=InetAddress.getLocalHost(); final String specials="/abcd!$&*()_-+.,=:;'~@[]?<>|#^%\"{}\\\u00a3`\u00ac\u00a6xyz"; // N.B. excludes space final URI uri = new URI(scheme, specials, host.getHostAddress(), 80, specials, specials, specials); final URI bld = URIBuilder.localhost() .setScheme(scheme) .setUserInfo(specials) .setPath(specials) .setCustomQuery(specials) .setFragment(specials) .build(); Assertions.assertEquals(uri.getHost(), bld.getHost()); Assertions.assertEquals(uri.getUserInfo(), bld.getUserInfo()); Assertions.assertEquals(uri.getPath(), bld.getPath()); Assertions.assertEquals(uri.getQuery(), bld.getQuery()); Assertions.assertEquals(uri.getFragment(), bld.getFragment()); } @Test public void testLoopbackAddress() throws Exception { // Check that the URI generated by URI builder agrees with that generated by using URI directly final String scheme="https"; final InetAddress host=InetAddress.getLoopbackAddress(); final String specials="/abcd!$&*()_-+.,=:;'~@[]?<>|#^%\"{}\\\u00a3`\u00ac\u00a6xyz"; // N.B. excludes space final URI uri = new URI(scheme, specials, host.getHostAddress(), 80, specials, specials, specials); final URI bld = URIBuilder.loopbackAddress() .setScheme(scheme) .setUserInfo(specials) .setPath(specials) .setCustomQuery(specials) .setFragment(specials) .build(); Assertions.assertEquals(uri.getHost(), bld.getHost()); Assertions.assertEquals(uri.getUserInfo(), bld.getUserInfo()); Assertions.assertEquals(uri.getPath(), bld.getPath()); Assertions.assertEquals(uri.getQuery(), bld.getQuery()); Assertions.assertEquals(uri.getFragment(), bld.getFragment()); } @Test public void testEmpty() throws Exception { final URIBuilder uribuilder = new URIBuilder(); final URI result = uribuilder.build(); Assertions.assertEquals(new URI(""), result); } @Test public void testEmptyPath() throws Exception { final URIBuilder uribuilder = new URIBuilder("http://thathost"); Assertions.assertTrue(uribuilder.isPathEmpty()); } @Test public void testRemoveParameter() throws Exception { final URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff&blah&blah", null); final URIBuilder uribuilder = new URIBuilder(uri); Assertions.assertFalse(uribuilder.isQueryEmpty()); Assertions.assertThrows(NullPointerException.class, () -> uribuilder.removeParameter(null)); uribuilder.removeParameter("DoesNotExist"); Assertions.assertEquals("stuff", uribuilder.getFirstQueryParam("param").getValue()); Assertions.assertNull(uribuilder.getFirstQueryParam("blah").getValue()); uribuilder.removeParameter("blah"); Assertions.assertEquals("stuff", uribuilder.getFirstQueryParam("param").getValue()); Assertions.assertNull(uribuilder.getFirstQueryParam("blah")); uribuilder.removeParameter("param"); Assertions.assertNull(uribuilder.getFirstQueryParam("param")); Assertions.assertTrue(uribuilder.isQueryEmpty()); uribuilder.removeParameter("AlreadyEmpty"); Assertions.assertTrue(uribuilder.isQueryEmpty()); Assertions.assertEquals(new URI("http://localhost:80/"), uribuilder.build()); } @Test public void testRemoveQuery() throws Exception { final URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff", null); final URIBuilder uribuilder = new URIBuilder(uri).removeQuery(); final URI result = uribuilder.build(); Assertions.assertEquals(new URI("http://localhost:80/"), result); } @Test public void testSetAuthorityFromNamedEndpointHost() throws Exception { final Host host = Host.create("localhost:88"); final URIBuilder uribuilder = new URIBuilder().setScheme(URIScheme.HTTP.id).setAuthority(host); // Check builder Assertions.assertNull(uribuilder.getUserInfo()); Assertions.assertEquals(host.getHostName(), uribuilder.getAuthority().getHostName()); Assertions.assertEquals(host.getHostName(), uribuilder.getHost()); // Check result final URI result = uribuilder.build(); Assertions.assertEquals(host.getHostName(), result.getHost()); Assertions.assertEquals(host.getPort(), result.getPort()); Assertions.assertEquals(new URI("http://localhost:88"), result); } @Test public void testSetAuthorityFromNamedEndpointHttpHost() throws Exception { final HttpHost httpHost = HttpHost.create("localhost:88"); final URIBuilder uribuilder = new URIBuilder().setScheme(URIScheme.HTTP.id).setAuthority(httpHost); // Check builder Assertions.assertNull(uribuilder.getUserInfo()); Assertions.assertEquals(httpHost.getHostName(), uribuilder.getAuthority().getHostName()); Assertions.assertEquals(httpHost.getHostName(), uribuilder.getHost()); // Check result final URI result = uribuilder.build(); Assertions.assertEquals(httpHost.getHostName(), result.getHost()); Assertions.assertEquals(httpHost.getPort(), result.getPort()); Assertions.assertEquals(new URI("http://localhost:88"), result); } @Test public void testSetAuthorityFromURIAuthority() throws Exception { final URIAuthority authority = URIAuthority.create("u:p@localhost:88"); final URIBuilder uribuilder = new URIBuilder().setScheme(URIScheme.HTTP.id).setAuthority(authority); // Check builder Assertions.assertEquals(authority.getUserInfo(), uribuilder.getAuthority().getUserInfo()); Assertions.assertEquals(authority.getHostName(), uribuilder.getAuthority().getHostName()); Assertions.assertEquals(authority.getHostName(), uribuilder.getHost()); // Check result final URI result = uribuilder.build(); Assertions.assertEquals(authority.getUserInfo(), result.getUserInfo()); Assertions.assertEquals(authority.getHostName(), result.getHost()); Assertions.assertEquals(authority.getPort(), result.getPort()); Assertions.assertEquals(authority.toString(), result.getAuthority()); Assertions.assertEquals(new URI("http://u:p@localhost:88"), result); } @Test public void testSetParameter() throws Exception { final URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff&blah&blah", null); final URIBuilder uribuilder = new URIBuilder(uri).setParameter("param", "some other stuff") .setParameter("blah", "blah") .setParameter("blah", "blah2"); final URI result = uribuilder.build(); Assertions.assertEquals(new URI("http://localhost:80/?param=some%20other%20stuff&blah=blah2"), result); } @Test public void testGetFirstNamedParameter() throws Exception { final URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff&blah&blah", null); URIBuilder uribuilder = new URIBuilder(uri).setParameter("param", "some other stuff") .setParameter("blah", "blah"); Assertions.assertEquals("some other stuff", uribuilder.getFirstQueryParam("param").getValue()); Assertions.assertEquals("blah", uribuilder.getFirstQueryParam("blah").getValue()); Assertions.assertNull(uribuilder.getFirstQueryParam("DoesNotExist")); // uribuilder = new URIBuilder("http://localhost:80/?param=some%20other%20stuff&blah=blah&blah=blah2"); Assertions.assertEquals("blah", uribuilder.getFirstQueryParam("blah").getValue()); } @Test public void testSetParametersWithEmptyArrayArg() throws Exception { final URI uri = new URI("http", null, "localhost", 80, "/test", "param=test", null); final URIBuilder uribuilder = new URIBuilder(uri).setParameters(); final URI result = uribuilder.build(); Assertions.assertEquals(new URI("http://localhost:80/test"), result); } @Test public void testSetParametersWithNullArrayArg() throws Exception { final URI uri = new URI("http", null, "localhost", 80, "/test", "param=test", null); final URIBuilder uribuilder = new URIBuilder(uri).setParameters((NameValuePair[]) null); final URI result = uribuilder.build(); Assertions.assertEquals(new URI("http://localhost:80/test"), result); } @Test public void testSetParametersWithEmptyList() throws Exception { final URI uri = new URI("http", null, "localhost", 80, "/test", "param=test", null); final URIBuilder uribuilder = new URIBuilder(uri).setParameters(Collections.emptyList()); final URI result = uribuilder.build(); Assertions.assertEquals(new URI("http://localhost:80/test"), result); } @Test public void testSetParametersWithNullList() throws Exception { final URI uri = new URI("http", null, "localhost", 80, "/test", "param=test", null); final URIBuilder uribuilder = new URIBuilder(uri).setParameters((List) null); final URI result = uribuilder.build(); Assertions.assertEquals(new URI("http://localhost:80/test"), result); } @Test public void testParameterWithSpecialChar() throws Exception { final URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff", null); final URIBuilder uribuilder = new URIBuilder(uri).addParameter("param", "1 + 1 = 2") .addParameter("param", "blah&blah"); final URI result = uribuilder.build(); Assertions.assertEquals(new URI("http://localhost:80/?param=stuff¶m=1%20%2B%201%20%3D%202&" + "param=blah%26blah"), result); } @Test public void testAddParameter() throws Exception { final URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff&blah&blah", null); final URIBuilder uribuilder = new URIBuilder(uri).addParameter("param", "some other stuff") .addParameter("blah", "blah"); final URI result = uribuilder.build(); Assertions.assertEquals(new URI("http://localhost:80/?param=stuff&blah&blah&" + "param=some%20other%20stuff&blah=blah"), result); } @Test public void testQueryEncoding() throws Exception { final URI uri1 = new URI("https://somehost.com/stuff?client_id=1234567890" + "&redirect_uri=https%3A%2F%2Fsomehost.com%2Fblah%20blah%2F"); final URI uri2 = new URIBuilder("https://somehost.com/stuff") .addParameter("client_id","1234567890") .addParameter("redirect_uri","https://somehost.com/blah blah/").build(); Assertions.assertEquals(uri1, uri2); } @Test public void testQueryAndParameterEncoding() throws Exception { final URI uri1 = new URI("https://somehost.com/stuff?param1=12345¶m2=67890"); final URI uri2 = new URIBuilder("https://somehost.com/stuff") .setCustomQuery("this&that") .addParameter("param1","12345") .addParameter("param2","67890").build(); Assertions.assertEquals(uri1, uri2); } @Test public void testPathEncoding() throws Exception { final URI uri1 = new URI("https://somehost.com/some%20path%20with%20blanks/"); final URI uri2 = new URIBuilder() .setScheme("https") .setHost("somehost.com") .setPath("/some path with blanks/") .build(); Assertions.assertEquals(uri1, uri2); } @Test public void testAgainstURI() throws Exception { // Check that the URI generated by URI builder agrees with that generated by using URI directly final String scheme="https"; final String host="localhost"; final String specials="/abcd!$&*()_-+.,=:;'~@[]?<>|#^%\"{}\\\u00a3`\u00ac\u00a6xyz"; // N.B. excludes space final URI uri = new URI(scheme, specials, host, 80, specials, specials, specials); final URI bld = new URIBuilder() .setScheme(scheme) .setHost(host) .setUserInfo(specials) .setPath(specials) .setCustomQuery(specials) .setFragment(specials) .build(); Assertions.assertEquals(uri.getHost(), bld.getHost()); Assertions.assertEquals(uri.getUserInfo(), bld.getUserInfo()); Assertions.assertEquals(uri.getPath(), bld.getPath()); Assertions.assertEquals(uri.getQuery(), bld.getQuery()); Assertions.assertEquals(uri.getFragment(), bld.getFragment()); } @Test public void testBuildAddParametersUTF8() throws Exception { assertAddParameters(StandardCharsets.UTF_8); } @Test public void testBuildAddParametersISO88591() throws Exception { assertAddParameters(StandardCharsets.ISO_8859_1); } public void assertAddParameters(final Charset charset) throws Exception { final URI uri = new URIBuilder("https://somehost.com/stuff") .setCharset(charset) .addParameters(createParameterList()).build(); assertBuild(charset, uri); // null addParameters final URI uri2 = new URIBuilder("https://somehost.com/stuff") .setCharset(charset) .addParameters(null).build(); Assertions.assertEquals("https://somehost.com/stuff", uri2.toString()); } @Test public void testBuildSetParametersUTF8() throws Exception { assertSetParameters(StandardCharsets.UTF_8); } @Test public void testBuildSetParametersISO88591() throws Exception { assertSetParameters(StandardCharsets.ISO_8859_1); } public void assertSetParameters(final Charset charset) throws Exception { final URI uri = new URIBuilder("https://somehost.com/stuff") .setCharset(charset) .setParameters(createParameterList()).build(); assertBuild(charset, uri); } public void assertBuild(final Charset charset, final URI uri) throws Exception { final String encodedData1 = PercentCodec.encode("\"1\u00aa position\"", charset); final String encodedData2 = PercentCodec.encode("Jos\u00e9 Abra\u00e3o", charset); final String uriExpected = String.format("https://somehost.com/stuff?parameter1=value1¶meter2=%s¶meter3=%s", encodedData1, encodedData2); Assertions.assertEquals(uriExpected, uri.toString()); } private List createParameterList() { final List parameters = new ArrayList<>(); parameters.add(new BasicNameValuePair("parameter1", "value1")); parameters.add(new BasicNameValuePair("parameter2", "\"1\u00aa position\"")); parameters.add(new BasicNameValuePair("parameter3", "Jos\u00e9 Abra\u00e3o")); return parameters; } @Test public void testMalformedPath() throws Exception { final String path = "@notexample.com/mypath"; final URI uri = new URIBuilder(path).setHost("example.com").build(); Assertions.assertEquals("example.com", uri.getHost()); } @Test public void testRelativePath() throws Exception { final URI uri = new URIBuilder("./mypath").build(); Assertions.assertEquals(new URI("./mypath"), uri); } @Test public void testRelativePathWithAuthority() throws Exception { final URI uri = new URIBuilder("./mypath").setHost("somehost").setScheme("http").build(); Assertions.assertEquals(new URI("http://somehost/./mypath"), uri); } @Test public void testTolerateNullInput() throws Exception { assertThat(new URIBuilder() .setScheme(null) .setHost("localhost") .setUserInfo(null) .setPort(8443) .setPath(null) .setCustomQuery(null) .setFragment(null) .build(), CoreMatchers.equalTo(URI.create("//localhost:8443"))); } @Test public void testTolerateBlankInput() throws Exception { assertThat(new URIBuilder() .setScheme("") .setHost("localhost") .setUserInfo("") .setPort(8443) .setPath("") .setPath("") .setCustomQuery("") .setFragment("") .build(), CoreMatchers.equalTo(URI.create("//localhost:8443"))); } @Test public void testHttpHost() throws Exception { final HttpHost httpHost = new HttpHost("http", "example.com", 1234); final URIBuilder uribuilder = new URIBuilder(); uribuilder.setHttpHost(httpHost); Assertions.assertEquals(URI.create("http://example.com:1234"), uribuilder.build()); } @Test public void testSetHostWithReservedChars() throws Exception { final URIBuilder uribuilder = new URIBuilder(); uribuilder.setScheme("http").setHost("!example!.com"); Assertions.assertEquals(URI.create("http://%21example%21.com"), uribuilder.build()); } @Test public void testGetHostWithReservedChars() throws Exception { final URIBuilder uribuilder = new URIBuilder("http://someuser%21@%21example%21.com/"); Assertions.assertEquals("!example!.com", uribuilder.getHost()); Assertions.assertEquals("someuser!", uribuilder.getUserInfo()); } @Test public void testMultipleLeadingPathSlashes() throws Exception { final URI uri = new URIBuilder() .setScheme("ftp") .setHost("somehost") .setPath("//blah//blah") .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("ftp://somehost//blah//blah"))); } @Test public void testNoAuthorityAndPath() throws Exception { final URI uri = new URIBuilder() .setScheme("file") .setPath("/blah") .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("file:/blah"))); } @Test public void testSetPathSegmentList() throws Exception { final URI uri = new URIBuilder() .setScheme("https") .setHost("somehost") .setPathSegments(Arrays.asList("api", "products")) .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("https://somehost/api/products"))); } @Test public void testSetPathSegmentsVarargs() throws Exception { final URI uri = new URIBuilder() .setScheme("https") .setHost("somehost") .setPathSegments("api", "products") .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("https://somehost/api/products"))); } @Test public void testSetPathSegmentsRootlessList() throws Exception { final URI uri = new URIBuilder() .setScheme("file") .setPathSegmentsRootless(Arrays.asList("dir", "foo")) .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("file:dir/foo"))); } @Test public void testSetPathSegmentsRootlessVarargs() throws Exception { final URI uri = new URIBuilder() .setScheme("file") .setPathSegmentsRootless("dir", "foo") .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("file:dir/foo"))); } @Test public void testAppendToExistingPath() throws Exception { final URI uri = new URIBuilder() .setScheme("https") .setHost("somehost") .setPath("api") .appendPath("v1/resources") .appendPath("idA") .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("https://somehost/api/v1/resources/idA"))); } @Test public void testAppendToNonExistingPath() throws Exception { final URI uri = new URIBuilder() .setScheme("https") .setHost("somehost") .appendPath("api/v2/customers") .appendPath("idA") .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("https://somehost/api/v2/customers/idA"))); } @Test public void testAppendNullToExistingPath() throws Exception { final URI uri = new URIBuilder() .setScheme("https") .setHost("somehost") .setPath("api") .appendPath(null) .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("https://somehost/api"))); } @Test public void testAppendNullToNonExistingPath() throws Exception { final URI uri = new URIBuilder() .setScheme("https") .setHost("somehost") .appendPath(null) .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("https://somehost"))); } @Test public void testAppendSegmentsVarargsToExistingPath() throws Exception { final URI uri = new URIBuilder() .setScheme("https") .setHost("myhost") .setPath("api") .appendPathSegments("v3", "products") .appendPathSegments("idA") .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("https://myhost/api/v3/products/idA"))); } @Test public void testAppendSegmentsVarargsToNonExistingPath() throws Exception { final URI uri = new URIBuilder() .setScheme("https") .setHost("somehost") .appendPathSegments("api", "v2", "customers") .appendPathSegments("idA") .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("https://somehost/api/v2/customers/idA"))); } @Test public void testAppendNullSegmentsVarargs() throws Exception { final String pathSegment = null; final URI uri = new URIBuilder() .setScheme("https") .setHost("somehost") .appendPathSegments(pathSegment) .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("https://somehost/"))); } @Test public void testAppendSegmentsListToExistingPath() throws Exception { final URI uri = new URIBuilder() .setScheme("http") .setHost("myhost") .setPath("api") .appendPathSegments(Arrays.asList("v3", "products")) .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("http://myhost/api/v3/products"))); } @Test public void testAppendSegmentsListToNonExistingPath() throws Exception { final URI uri = new URIBuilder() .setScheme("http") .setHost("myhost") .appendPathSegments(Arrays.asList("api", "v3", "customers")) .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("http://myhost/api/v3/customers"))); } @Test public void testAppendNullSegmentsList() throws Exception { final List pathSegments = null; final URI uri = new URIBuilder() .setScheme("http") .setHost("myhost") .appendPathSegments(pathSegments) .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("http://myhost"))); } @Test public void testNoAuthorityAndPathSegments() throws Exception { final URI uri = new URIBuilder() .setScheme("file") .setPathSegments("this", "that") .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("file:/this/that"))); } @Test public void testNoAuthorityAndRootlessPath() throws Exception { final URI uri = new URIBuilder() .setScheme("file") .setPath("blah") .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("file:blah"))); } @Test public void testNoAuthorityAndRootlessPathSegments() throws Exception { final URI uri = new URIBuilder() .setScheme("file") .setPathSegmentsRootless("this", "that") .build(); assertThat(uri, CoreMatchers.equalTo(URI.create("file:this/that"))); } @Test public void testOpaque() throws Exception { final URIBuilder uriBuilder = new URIBuilder("http://host.com"); final URI uri = uriBuilder.build(); assertThat(uriBuilder.isOpaque(), CoreMatchers.equalTo(uri.isOpaque())); } @Test public void testAddParameterEncodingEquivalence() throws Exception { final URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff with spaces", null); final URIBuilder uribuilder = new URIBuilder().setScheme("http").setHost("localhost").setPort(80).setPath("/").addParameter( "param", "stuff with spaces"); final URI result = uribuilder.build(); Assertions.assertEquals(uri, result); } @Test public void testSchemeSpecificPartParametersNull() throws Exception { final URIBuilder uribuilder = new URIBuilder("http://host.com").setParameter("par", "parvalue") .setSchemeSpecificPart("", (NameValuePair)null); Assertions.assertEquals(new URI("http://host.com?par=parvalue"), uribuilder.build()); } @Test public void testSchemeSpecificPartSetGet() throws Exception { final URIBuilder uribuilder = new URIBuilder().setSchemeSpecificPart("specificpart"); Assertions.assertEquals("specificpart", uribuilder.getSchemeSpecificPart()); } /** Common use case: mailto: scheme. See https://tools.ietf.org/html/rfc6068#section-2 */ @Test public void testSchemeSpecificPartNameValuePairByRFC6068Sample() throws Exception { final URIBuilder uribuilder = new URIBuilder().setScheme("mailto") .setSchemeSpecificPart("my@email.server", new BasicNameValuePair("subject", "mail subject")); final String result = uribuilder.build().toString(); Assertions.assertTrue(result.contains("my@email.server"), "mail address as scheme specific part expected"); Assertions.assertTrue(result.contains("mail%20subject"), "correct parameter encoding expected for that scheme"); } /** Common use case: mailto: scheme. See https://tools.ietf.org/html/rfc6068#section-2 */ @Test public void testSchemeSpecificPartNameValuePairListByRFC6068Sample() throws Exception { final List parameters = new ArrayList<>(); parameters.add(new BasicNameValuePair("subject", "mail subject")); final URIBuilder uribuilder = new URIBuilder().setScheme("mailto").setSchemeSpecificPart("my@email.server", parameters); final String result = uribuilder.build().toString(); Assertions.assertTrue(result.contains("my@email.server"), "mail address as scheme specific part expected"); Assertions.assertTrue(result.contains("mail%20subject"), "correct parameter encoding expected for that scheme"); } @Test public void testNormalizeSyntax() throws Exception { Assertions.assertEquals("example://a/b/c/%7Bfoo%7D", new URIBuilder("eXAMPLE://a/./b/../b/%63/%7bfoo%7d").normalizeSyntax().build().toASCIIString()); Assertions.assertEquals("http://www.example.com/%3C", new URIBuilder("http://www.example.com/%3c").normalizeSyntax().build().toASCIIString()); Assertions.assertEquals("http://www.example.com/", new URIBuilder("HTTP://www.EXAMPLE.com/").normalizeSyntax().build().toASCIIString()); Assertions.assertEquals("http://www.example.com/a%2F", new URIBuilder("http://www.example.com/a%2f").normalizeSyntax().build().toASCIIString()); Assertions.assertEquals("http://www.example.com/?a%2F", new URIBuilder("http://www.example.com/?a%2f").normalizeSyntax().build().toASCIIString()); Assertions.assertEquals("http://www.example.com/?q=%26", new URIBuilder("http://www.example.com/?q=%26").normalizeSyntax().build().toASCIIString()); Assertions.assertEquals("http://www.example.com/%23?q=%26", new URIBuilder("http://www.example.com/%23?q=%26").normalizeSyntax().build().toASCIIString()); Assertions.assertEquals("http://www.example.com/blah-%28%20-blah-%20%26%20-blah-%20%29-blah/", new URIBuilder("http://www.example.com/blah-%28%20-blah-%20&%20-blah-%20)-blah/").normalizeSyntax().build().toASCIIString()); Assertions.assertEquals("../../.././", new URIBuilder("../../.././").normalizeSyntax().build().toASCIIString()); Assertions.assertEquals("file:../../.././", new URIBuilder("file:../../.././").normalizeSyntax().build().toASCIIString()); Assertions.assertEquals("http://host/", new URIBuilder("http://host/../../.././").normalizeSyntax().build().toASCIIString()); Assertions.assertEquals("http:/", new URIBuilder("http:///../../.././").normalizeSyntax().build().toASCIIString()); } @Test public void testIpv6Host() throws Exception { final URIBuilder builder = new URIBuilder("https://[::1]:432/path"); final URI uri = builder.build(); Assertions.assertEquals(432, builder.getPort()); Assertions.assertEquals(432, uri.getPort()); Assertions.assertEquals("https", builder.getScheme()); Assertions.assertEquals("https", uri.getScheme()); Assertions.assertEquals("::1", builder.getHost()); Assertions.assertEquals("[::1]", uri.getHost()); Assertions.assertEquals("/path", builder.getPath()); Assertions.assertEquals("/path", uri.getPath()); } @Test public void testIpv6HostWithPortUpdate() throws Exception { // Updating the port clears URIBuilder.encodedSchemeSpecificPart // and bypasses the fast/simple path which preserves input. final URIBuilder builder = new URIBuilder("https://[::1]:432/path").setPort(123); final URI uri = builder.build(); Assertions.assertEquals(123, builder.getPort()); Assertions.assertEquals(123, uri.getPort()); Assertions.assertEquals("https", builder.getScheme()); Assertions.assertEquals("https", uri.getScheme()); Assertions.assertEquals("::1", builder.getHost()); Assertions.assertEquals("[::1]", uri.getHost()); Assertions.assertEquals("/path", builder.getPath()); Assertions.assertEquals("/path", uri.getPath()); } @Test public void testBuilderWithUnbracketedIpv6Host() throws Exception { final URIBuilder builder = new URIBuilder().setScheme("https").setHost("::1").setPort(443).setPath("/path"); final URI uri = builder.build(); Assertions.assertEquals("https", builder.getScheme()); Assertions.assertEquals("https", uri.getScheme()); Assertions.assertEquals(443, builder.getPort()); Assertions.assertEquals(443, uri.getPort()); Assertions.assertEquals("::1", builder.getHost()); Assertions.assertEquals("[::1]", uri.getHost()); Assertions.assertEquals("/path", builder.getPath()); Assertions.assertEquals("/path", uri.getPath()); } } httpcore5/src/test/java/org/apache/hc/core5/net/TestHost.java0100664 0000000 0000000 00000012416 14435411677 023060 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.net; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.URISyntaxException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link Host}. * */ public class TestHost { @Test public void testConstructor() { final Host host1 = new Host("somehost", 8080); Assertions.assertEquals("somehost", host1.getHostName()); Assertions.assertEquals(8080, host1.getPort()); final Host host2 = new Host("somehost", 0); Assertions.assertEquals("somehost", host2.getHostName()); Assertions.assertEquals(0, host2.getPort()); Assertions.assertThrows(NullPointerException.class, () -> new Host(null, 0)); } @Test public void testHashCode() throws Exception { final Host host1 = new Host("somehost", 8080); final Host host2 = new Host("somehost", 80); final Host host3 = new Host("someotherhost", 8080); final Host host4 = new Host("somehost", 80); Assertions.assertEquals(host1.hashCode(), host1.hashCode()); Assertions.assertTrue(host1.hashCode() != host2.hashCode()); Assertions.assertTrue(host1.hashCode() != host3.hashCode()); Assertions.assertEquals(host2.hashCode(), host4.hashCode()); } @Test public void testEquals() throws Exception { final Host host1 = new Host("somehost", 8080); final Host host2 = new Host("somehost", 80); final Host host3 = new Host("someotherhost", 8080); final Host host4 = new Host("somehost", 80); Assertions.assertEquals(host1, host1); Assertions.assertNotEquals(host1, host2); Assertions.assertNotEquals(host1, host3); Assertions.assertEquals(host2, host4); } @Test public void testToString() throws Exception { final Host host1 = new Host("somehost", 8888); Assertions.assertEquals("somehost:8888", host1.toString()); } @Test public void testSerialization() throws Exception { final Host orig = new Host("somehost", 8080); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer); outStream.writeObject(orig); outStream.close(); final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final Host clone = (Host) inStream.readObject(); Assertions.assertEquals(orig, clone); } @Test public void testCreateFromString() throws Exception { Assertions.assertEquals(new Host("somehost", 8080), Host.create("somehost:8080")); Assertions.assertEquals(new Host("somehost", 1234), Host.create("somehost:1234")); Assertions.assertEquals(new Host("somehost", 0), Host.create("somehost:0")); } @Test public void testCreateFromStringInvalid() throws Exception { Assertions.assertThrows(URISyntaxException.class, () -> Host.create(" host ")); Assertions.assertThrows(URISyntaxException.class, () -> Host.create("host :8080")); Assertions.assertThrows(IllegalArgumentException.class, () -> Host.create("")); } @Test public void testIpv6HostAndPort() throws Exception { final Host host = Host.create("[::1]:80"); Assertions.assertEquals("::1", host.getHostName()); Assertions.assertEquals(80, host.getPort()); } @Test public void testIpv6HostAndPortWithoutBrackets() { // ambiguous Assertions.assertThrows(URISyntaxException.class, () -> Host.create("::1:80")); } @Test public void testIpv6HostWithoutPort() { Assertions.assertThrows(URISyntaxException.class, () -> Host.create("::1")); } @Test public void testIpv6HostToString() { Assertions.assertEquals("[::1]:80", new Host("::1", 80).toString()); Assertions.assertEquals("[::1]", new Host("::1", -1).toString()); } } httpcore5/src/test/java/org/apache/hc/core5/reactor/0040775 0000000 0000000 00000000000 14435411752 021302 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/reactor/IOReactorConfigTest.java0100664 0000000 0000000 00000006506 14403631147 025763 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.net.InetSocketAddress; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class IOReactorConfigTest { @Test public void testCustomIOReactorConfig() throws Exception { final IOReactorConfig reactorConfig = IOReactorConfig.custom() .setSelectInterval(TimeValue.ofMilliseconds(500)) .setIoThreadCount(2) .setSoTimeout(Timeout.ofSeconds(10)) .setSoReuseAddress(true) .setSoLinger(TimeValue.ofSeconds(30)) .setSoKeepAlive(true) .setTcpNoDelay(false) .setTrafficClass(0x02) .setSndBufSize(32767) .setRcvBufSize(8192) .setBacklogSize(5) .setSocksProxyAddress(new InetSocketAddress(8888)) .setSocksProxyUsername("socksProxyUsername") .setSocksProxyPassword("socksProxyPassword") .build(); Assertions.assertEquals(TimeValue.ofMilliseconds(500), reactorConfig.getSelectInterval()); Assertions.assertEquals(2, reactorConfig.getIoThreadCount()); Assertions.assertEquals(Timeout.ofSeconds(10), reactorConfig.getSoTimeout()); Assertions.assertTrue(reactorConfig.isSoReuseAddress()); Assertions.assertEquals(TimeValue.ofSeconds(30), reactorConfig.getSoLinger()); Assertions.assertTrue(reactorConfig.isSoKeepAlive()); Assertions.assertFalse(reactorConfig.isTcpNoDelay()); Assertions.assertEquals(0x02, reactorConfig.getTrafficClass()); Assertions.assertEquals(32767, reactorConfig.getSndBufSize()); Assertions.assertEquals(8192, reactorConfig.getRcvBufSize()); Assertions.assertEquals(5, reactorConfig.getBacklogSize()); Assertions.assertEquals(new InetSocketAddress(8888), reactorConfig.getSocksProxyAddress()); Assertions.assertEquals("socksProxyUsername", reactorConfig.getSocksProxyUsername()); Assertions.assertEquals("socksProxyPassword", reactorConfig.getSocksProxyPassword()); } } httpcore5/src/test/java/org/apache/hc/core5/reactor/TestAbstractIOSessionPool.java0100664 0000000 0000000 00000026610 14403631147 027175 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import static org.hamcrest.MatcherAssert.assertThat; import java.net.UnknownHostException; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; public class TestAbstractIOSessionPool { @Mock private Future connectFuture; @Mock private FutureCallback callback1; @Mock private FutureCallback callback2; @Mock private IOSession ioSession1; @Mock private IOSession ioSession2; @Captor ArgumentCaptor> connectCallbackCaptor; private AbstractIOSessionPool impl; @BeforeEach public void setup() { MockitoAnnotations.openMocks(this); impl = Mockito.mock(AbstractIOSessionPool.class, Mockito.withSettings() .defaultAnswer(Answers.CALLS_REAL_METHODS) .useConstructor()); } @Test public void testGetSessions() throws Exception { Mockito.when(impl.connectSession( ArgumentMatchers.anyString(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(connectFuture); Mockito.doAnswer(invocation -> { final Callback callback = invocation.getArgument(1); callback.execute(true); return null; }).when(impl).validateSession(ArgumentMatchers.any(), ArgumentMatchers.any()); Mockito.when(ioSession1.isOpen()).thenReturn(true); final Future future1 = impl.getSession("somehost", Timeout.ofSeconds(123L), null); assertThat(future1, CoreMatchers.notNullValue()); assertThat(future1.isDone(), CoreMatchers.equalTo(false)); assertThat(impl.getRoutes(), CoreMatchers.hasItem("somehost")); Mockito.verify(impl).connectSession( ArgumentMatchers.eq("somehost"), ArgumentMatchers.eq(Timeout.ofSeconds(123L)), ArgumentMatchers.any()); final Future future2 = impl.getSession("somehost", Timeout.ofSeconds(123L), null); assertThat(future2, CoreMatchers.notNullValue()); assertThat(future2.isDone(), CoreMatchers.equalTo(false)); assertThat(impl.getRoutes(), CoreMatchers.hasItem("somehost")); Mockito.verify(impl, Mockito.times(1)).connectSession( ArgumentMatchers.eq("somehost"), ArgumentMatchers.any(), ArgumentMatchers.argThat(callback -> { callback.completed(ioSession1); return true; })); assertThat(future1.isDone(), CoreMatchers.equalTo(true)); assertThat(future1.get(), CoreMatchers.sameInstance(ioSession1)); assertThat(future2.isDone(), CoreMatchers.equalTo(true)); assertThat(future2.get(), CoreMatchers.sameInstance(ioSession1)); Mockito.verify(impl, Mockito.times(2)).validateSession(ArgumentMatchers.any(), ArgumentMatchers.any()); final Future future3 = impl.getSession("somehost", Timeout.ofSeconds(123L), null); Mockito.verify(impl, Mockito.times(1)).connectSession( ArgumentMatchers.eq("somehost"), ArgumentMatchers.any(), ArgumentMatchers.any()); Mockito.verify(impl, Mockito.times(3)).validateSession(ArgumentMatchers.any(), ArgumentMatchers.any()); assertThat(future3.isDone(), CoreMatchers.equalTo(true)); assertThat(future3.get(), CoreMatchers.sameInstance(ioSession1)); } @Test public void testGetSessionConnectFailure() throws Exception { Mockito.when(impl.connectSession( ArgumentMatchers.anyString(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(connectFuture); final Future future1 = impl.getSession("somehost", Timeout.ofSeconds(123L), null); assertThat(future1, CoreMatchers.notNullValue()); assertThat(future1.isDone(), CoreMatchers.equalTo(false)); assertThat(impl.getRoutes(), CoreMatchers.hasItem("somehost")); Mockito.verify(impl).connectSession( ArgumentMatchers.eq("somehost"), ArgumentMatchers.eq(Timeout.ofSeconds(123L)), connectCallbackCaptor.capture()); final Future future2 = impl.getSession("somehost", Timeout.ofSeconds(123L), null); assertThat(future2, CoreMatchers.notNullValue()); assertThat(future2.isDone(), CoreMatchers.equalTo(false)); assertThat(impl.getRoutes(), CoreMatchers.hasItem("somehost")); final FutureCallback connectCallback = connectCallbackCaptor.getValue(); Assertions.assertNotNull(connectCallback); connectCallback.failed(new Exception("Boom")); // Ensure connect failure invalidates all pending futures assertThat(future1.isDone(), CoreMatchers.equalTo(true)); assertThat(future2.isDone(), CoreMatchers.equalTo(true)); } @Test public void testShutdownPool() throws Exception { final AbstractIOSessionPool.PoolEntry entry1 = impl.getPoolEntry("host1"); assertThat(entry1, CoreMatchers.notNullValue()); entry1.session = ioSession1; final AbstractIOSessionPool.PoolEntry entry2 = impl.getPoolEntry("host2"); assertThat(entry2, CoreMatchers.notNullValue()); entry2.session = ioSession2; final AbstractIOSessionPool.PoolEntry entry3 = impl.getPoolEntry("host3"); assertThat(entry3, CoreMatchers.notNullValue()); entry3.sessionFuture = connectFuture; entry3.requestQueue.add(callback1); entry3.requestQueue.add(callback2); impl.close(CloseMode.GRACEFUL); Mockito.verify(impl).closeSession(ioSession1, CloseMode.GRACEFUL); Mockito.verify(impl).closeSession(ioSession2, CloseMode.GRACEFUL); Mockito.verify(connectFuture).cancel(ArgumentMatchers.anyBoolean()); Mockito.verify(callback1).cancelled(); Mockito.verify(callback2).cancelled(); } @Test public void testCloseIdleSessions() throws Exception { final AbstractIOSessionPool.PoolEntry entry1 = impl.getPoolEntry("host1"); assertThat(entry1, CoreMatchers.notNullValue()); entry1.session = ioSession1; final AbstractIOSessionPool.PoolEntry entry2 = impl.getPoolEntry("host2"); assertThat(entry2, CoreMatchers.notNullValue()); entry2.session = ioSession2; impl.closeIdle(TimeValue.ZERO_MILLISECONDS); Mockito.verify(impl).closeSession(ioSession1, CloseMode.GRACEFUL); Mockito.verify(impl).closeSession(ioSession2, CloseMode.GRACEFUL); assertThat(entry1.session, CoreMatchers.nullValue()); assertThat(entry2.session, CoreMatchers.nullValue()); } @Test public void testEnumSessions() throws Exception { final AbstractIOSessionPool.PoolEntry entry1 = impl.getPoolEntry("host1"); assertThat(entry1, CoreMatchers.notNullValue()); entry1.session = ioSession1; final AbstractIOSessionPool.PoolEntry entry2 = impl.getPoolEntry("host2"); assertThat(entry2, CoreMatchers.notNullValue()); entry2.session = ioSession2; impl.enumAvailable(ioSession -> ioSession.close(CloseMode.GRACEFUL)); Mockito.verify(ioSession1).close(CloseMode.GRACEFUL); Mockito.verify(ioSession2).close(CloseMode.GRACEFUL); } @Test public void testGetSessionReconnectAfterValidate() throws Exception { final AbstractIOSessionPool.PoolEntry entry1 = impl.getPoolEntry("somehost"); assertThat(entry1, CoreMatchers.notNullValue()); entry1.session = ioSession1; Mockito.when(ioSession1.isOpen()).thenReturn(true); Mockito.doAnswer(invocation -> { final Callback callback = invocation.getArgument(1); callback.execute(false); return null; }).when(impl).validateSession(ArgumentMatchers.any(), ArgumentMatchers.any()); impl.getSession("somehost", Timeout.ofSeconds(123L), null); Mockito.verify(impl, Mockito.times(1)).connectSession( ArgumentMatchers.eq("somehost"), ArgumentMatchers.eq(Timeout.ofSeconds(123L)), ArgumentMatchers.any()); } @Test public void testGetSessionReconnectIfClosed() throws Exception { final AbstractIOSessionPool.PoolEntry entry1 = impl.getPoolEntry("somehost"); assertThat(entry1, CoreMatchers.notNullValue()); entry1.session = ioSession1; Mockito.when(ioSession1.isOpen()).thenReturn(false); impl.getSession("somehost", Timeout.ofSeconds(123L), null); Mockito.verify(impl).connectSession( ArgumentMatchers.eq("somehost"), ArgumentMatchers.eq(Timeout.ofSeconds(123L)), ArgumentMatchers.any()); } @Test public void testGetSessionConnectUnknownHost() throws Exception { Mockito.when(connectFuture.isDone()).thenReturn(true); Mockito.when(impl.connectSession( ArgumentMatchers.anyString(), ArgumentMatchers.any(), ArgumentMatchers.argThat(callback -> { callback.failed(new UnknownHostException("Boom")); return true; }))).thenReturn(connectFuture); final Future future1 = impl.getSession("somehost", Timeout.ofSeconds(123L), null); assertThat(future1, CoreMatchers.notNullValue()); assertThat(future1.isDone(), CoreMatchers.equalTo(true)); final Future future2 = impl.getSession("somehost", Timeout.ofSeconds(123L), null); assertThat(future2, CoreMatchers.notNullValue()); assertThat(future2.isDone(), CoreMatchers.equalTo(true)); } } httpcore5/src/test/java/org/apache/hc/core5/reactor/ssl/0040775 0000000 0000000 00000000000 14435411752 022103 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/reactor/ssl/SSLIOSessionTest.java0100664 0000000 0000000 00000017057 14435411752 026052 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor.ssl; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import java.nio.ByteBuffer; import java.security.Provider; import java.util.concurrent.locks.ReentrantLock; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContextSpi; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; class SSLIOSessionTest { // Define common variables here, so you can easily modify them in each test private NamedEndpoint targetEndpoint; private IOSession ioSession; private SSLMode sslMode; private SSLContext sslContext; private SSLBufferMode sslBufferMode; private SSLSessionInitializer initializer; private SSLSessionVerifier verifier; private Timeout handshakeTimeout; private Callback sessionStartCallback; private Callback sessionEndCallback; private FutureCallback resultCallback; private IOEventHandler ioEventHandler; private SSLEngine mockSSLEngine; private SSLSession sslSession; @BeforeEach public void setUp() throws SSLException { final String protocol = "TestProtocol"; // Arrange targetEndpoint = mock(NamedEndpoint.class); ioSession = mock(IOSession.class); sslMode = SSLMode.CLIENT; // Use actual SSLMode //SSLContext sslContext = SSLContext.getDefault(); final SSLContextSpi sslContextSpi = mock(SSLContextSpi.class); final Provider provider = mock(Provider.class); sslContext = new TestSSLContext(sslContextSpi, provider, protocol); sslSession = mock(SSLSession.class); sslBufferMode = SSLBufferMode.STATIC; initializer = mock(SSLSessionInitializer.class); verifier = mock(SSLSessionVerifier.class); handshakeTimeout = mock(Timeout.class); sessionStartCallback = mock(Callback.class); sessionEndCallback = mock(Callback.class); resultCallback = mock(FutureCallback.class); ioEventHandler = mock(IOEventHandler.class); // Mock behavior of targetEndpoint Mockito.when(targetEndpoint.getHostName()).thenReturn("testHostName"); Mockito.when(targetEndpoint.getPort()).thenReturn(8080); Mockito.when(sslSession.getPacketBufferSize()).thenReturn(1024); Mockito.when(sslSession.getApplicationBufferSize()).thenReturn(1024); // Mock behavior of ioSession Mockito.when(ioSession.getEventMask()).thenReturn(1); Mockito.when(ioSession.getLock()).thenReturn(new ReentrantLock()); // Mock behavior of sslContext and SSLEngine mockSSLEngine = mock(SSLEngine.class); Mockito.when(sslContext.createSSLEngine(any(String.class), any(Integer.class))).thenReturn(mockSSLEngine); Mockito.when(mockSSLEngine.getSession()).thenReturn(sslSession); Mockito.when(mockSSLEngine.getHandshakeStatus()).thenReturn(SSLEngineResult.HandshakeStatus.NEED_WRAP); Mockito.when(ioSession.getHandler()).thenReturn(ioEventHandler); final SSLEngineResult mockResult = new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.FINISHED, 0, 464); Mockito.when(mockSSLEngine.wrap(any(ByteBuffer.class), any(ByteBuffer.class))).thenReturn(mockResult); } @Test void testConstructorWhenSSLEngineOk() { final String protocol = "TestProtocol"; // Arrange Mockito.when(mockSSLEngine.getApplicationProtocol()).thenReturn(protocol); // Act final TestableSSLIOSession sslioSession = new TestableSSLIOSession(targetEndpoint, ioSession, sslMode, sslContext, sslBufferMode, initializer, verifier, handshakeTimeout, sessionStartCallback, sessionEndCallback, resultCallback); // Assert Assertions.assertDoesNotThrow(() -> sslioSession.beginHandshake(ioSession)); Assertions.assertEquals(protocol, sslioSession.getTlsDetails().getApplicationProtocol()); } @Test void testConstructorWhenSSLEngineThrowsException() { final String protocol = "http/1.1"; // Arrange Mockito.when(mockSSLEngine.getApplicationProtocol()).thenThrow(UnsupportedOperationException.class); // Act final TestableSSLIOSession sslioSession = new TestableSSLIOSession(targetEndpoint, ioSession, sslMode, sslContext, sslBufferMode, initializer, verifier, handshakeTimeout, sessionStartCallback, sessionEndCallback, resultCallback); // Assert Assertions.assertDoesNotThrow(() -> sslioSession.beginHandshake(ioSession)); Assertions.assertEquals(protocol, sslioSession.getTlsDetails().getApplicationProtocol()); } static class TestSSLContext extends SSLContext { /** * Creates an SSLContext object. * * @param contextSpi the delegate * @param provider the provider * @param protocol the protocol */ protected TestSSLContext(final SSLContextSpi contextSpi, final Provider provider, final String protocol) { super(contextSpi, provider, protocol); } } static class TestableSSLIOSession extends SSLIOSession { TestableSSLIOSession(final NamedEndpoint targetEndpoint, final IOSession session, final SSLMode sslMode, final SSLContext sslContext, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier, final Timeout handshakeTimeout, final Callback sessionStartCallback, final Callback sessionEndCallback, final FutureCallback resultCallback) { super(targetEndpoint, session, sslMode, sslContext, sslBufferMode, initializer, verifier, handshakeTimeout, sessionStartCallback, sessionEndCallback, resultCallback); } } } httpcore5/src/test/java/org/apache/hc/core5/reactor/IOWorkersTest.java0100664 0000000 0000000 00000003373 14403631147 024671 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import static org.mockito.Mockito.mock; import org.junit.jupiter.api.Test; public class IOWorkersTest { @Test public void testIndexOverflow() { final SingleCoreIOReactor reactor = new SingleCoreIOReactor(null, mock(IOEventHandlerFactory.class), IOReactorConfig.DEFAULT, null, null, null); final IOWorkers.Selector selector = IOWorkers.newSelector(new SingleCoreIOReactor[]{reactor, reactor, reactor}); for (long i = Integer.MAX_VALUE - 10; i < (long) Integer.MAX_VALUE + 10; i++) { selector.next(); } } } httpcore5/src/test/java/org/apache/hc/core5/util/0040775 0000000 0000000 00000000000 14403631147 020615 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/util/TestDeadlineTimeoutException.java0100664 0000000 0000000 00000003452 14403631147 027254 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDeadlineTimeoutException { @Test public void testMessage() { final Deadline deadline = Deadline.fromUnixMilliseconds(1000).freeze(); Assertions.assertTrue(deadline.isExpired(), deadline.toString()); final String format = deadline.formatTarget(); final TimeValue diff = TimeValue.ofMilliseconds(deadline.remaining()); Assertions.assertEquals("Deadline: " + format + ", " + diff + " overdue", DeadlineTimeoutException.from(deadline).getMessage()); } } httpcore5/src/test/java/org/apache/hc/core5/util/TestCharArrayBuffer.java0100664 0000000 0000000 00000032267 14403631147 025335 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link CharArrayBuffer}. * */ public class TestCharArrayBuffer { @Test public void testConstructor() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(16); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(0, buffer.length()); Assertions.assertNotNull(buffer.array()); Assertions.assertEquals(16, buffer.array().length); Assertions.assertThrows(IllegalArgumentException.class, () -> new CharArrayBuffer(-1)); } @Test public void testSimpleAppend() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(16); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(0, buffer.length()); final char[] b1 = buffer.toCharArray(); Assertions.assertNotNull(b1); Assertions.assertEquals(0, b1.length); Assertions.assertTrue(buffer.isEmpty()); Assertions.assertFalse(buffer.isFull()); final char[] tmp = new char[] { '1', '2', '3', '4'}; buffer.append(tmp, 0, tmp.length); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(4, buffer.length()); Assertions.assertFalse(buffer.isEmpty()); Assertions.assertFalse(buffer.isFull()); final char[] b2 = buffer.toCharArray(); Assertions.assertNotNull(b2); Assertions.assertEquals(4, b2.length); for (int i = 0; i < tmp.length; i++) { Assertions.assertEquals(tmp[i], b2[i]); Assertions.assertEquals(tmp[i], buffer.charAt(i)); } Assertions.assertEquals("1234", buffer.toString()); buffer.clear(); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(0, buffer.length()); Assertions.assertTrue(buffer.isEmpty()); Assertions.assertFalse(buffer.isFull()); } @Test public void testExpandAppend() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(4); Assertions.assertEquals(4, buffer.capacity()); final char[] tmp = new char[] { '1', '2', '3', '4'}; buffer.append(tmp, 0, 2); buffer.append(tmp, 0, 4); buffer.append(tmp, 0, 0); Assertions.assertEquals(8, buffer.capacity()); Assertions.assertEquals(6, buffer.length()); buffer.append(tmp, 0, 4); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(10, buffer.length()); Assertions.assertEquals("1212341234", buffer.toString()); } @Test public void testAppendString() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(8); buffer.append("stuff"); buffer.append(" and more stuff"); Assertions.assertEquals("stuff and more stuff", buffer.toString()); } @Test public void testAppendNullString() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(8); buffer.append((String)null); Assertions.assertEquals("null", buffer.toString()); } @Test public void testAppendCharArrayBuffer() throws Exception { final CharArrayBuffer buffer1 = new CharArrayBuffer(8); buffer1.append(" and more stuff"); final CharArrayBuffer buffer2 = new CharArrayBuffer(8); buffer2.append("stuff"); buffer2.append(buffer1); Assertions.assertEquals("stuff and more stuff", buffer2.toString()); } @Test public void testAppendNullCharArrayBuffer() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(8); buffer.append((CharArrayBuffer)null); buffer.append((CharArrayBuffer)null, 0, 0); Assertions.assertEquals("", buffer.toString()); } @Test public void testAppendSingleChar() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(4); buffer.append('1'); buffer.append('2'); buffer.append('3'); buffer.append('4'); buffer.append('5'); buffer.append('6'); Assertions.assertEquals("123456", buffer.toString()); } @Test public void testInvalidCharArrayAppend() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(4); buffer.append((char[])null, 0, 0); final char[] tmp = new char[] { '1', '2', '3', '4'}; Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, -1, 0)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, -1)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, 8)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 10, Integer.MAX_VALUE)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 2, 4)); } @Test public void testSetLength() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(4); buffer.setLength(2); Assertions.assertEquals(2, buffer.length()); } @Test public void testSetInvalidLength() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(4); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.setLength(-2)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.setLength(200)); } @Test public void testEnsureCapacity() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(4); buffer.ensureCapacity(2); Assertions.assertEquals(4, buffer.capacity()); buffer.ensureCapacity(8); Assertions.assertEquals(8, buffer.capacity()); } @Test public void testIndexOf() { final CharArrayBuffer buffer = new CharArrayBuffer(16); buffer.append("name: value"); Assertions.assertEquals(4, buffer.indexOf(':')); Assertions.assertEquals(-1, buffer.indexOf(',')); Assertions.assertEquals(4, buffer.indexOf(':', -1, 11)); Assertions.assertEquals(4, buffer.indexOf(':', 0, 1000)); Assertions.assertEquals(-1, buffer.indexOf(':', 2, 1)); } @Test public void testSubstring() { final CharArrayBuffer buffer = new CharArrayBuffer(16); buffer.append(" name: value "); Assertions.assertEquals(5, buffer.indexOf(':')); Assertions.assertEquals(" name", buffer.substring(0, 5)); Assertions.assertEquals(" value ", buffer.substring(6, buffer.length())); Assertions.assertEquals("name", buffer.substringTrimmed(0, 5)); Assertions.assertEquals("value", buffer.substringTrimmed(6, buffer.length())); Assertions.assertEquals("", buffer.substringTrimmed(13, buffer.length())); } @Test public void testSubstringIndexOfOutBound() { final CharArrayBuffer buffer = new CharArrayBuffer(16); buffer.append("stuff"); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.substring(-2, 10)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.substringTrimmed(-2, 10)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.substring(12, 10)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.substringTrimmed(12, 10)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.substring(2, 1)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.substringTrimmed(2, 1)); } @Test public void testAppendAsciiByteArray() throws Exception { final String s1 = "stuff"; final String s2 = " and more stuff"; final byte[] b1 = s1.getBytes(StandardCharsets.US_ASCII); final byte[] b2 = s2.getBytes(StandardCharsets.US_ASCII); final CharArrayBuffer buffer = new CharArrayBuffer(8); buffer.append(b1, 0, b1.length); buffer.append(b2, 0, b2.length); Assertions.assertEquals("stuff and more stuff", buffer.toString()); } @Test public void testAppendISOByteArray() throws Exception { final byte[] b = new byte[] {0x00, 0x20, 0x7F, -0x80, -0x01}; final CharArrayBuffer buffer = new CharArrayBuffer(8); buffer.append(b, 0, b.length); final char[] ch = buffer.toCharArray(); Assertions.assertNotNull(ch); Assertions.assertEquals(5, ch.length); Assertions.assertEquals(0x00, ch[0]); Assertions.assertEquals(0x20, ch[1]); Assertions.assertEquals(0x7F, ch[2]); Assertions.assertEquals(0x80, ch[3]); Assertions.assertEquals(0xFF, ch[4]); } @Test public void testAppendNullByteArray() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(8); buffer.append((byte[])null, 0, 0); Assertions.assertEquals("", buffer.toString()); } @Test public void testAppendNullByteArrayBuffer() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(8); buffer.append((ByteArrayBuffer)null, 0, 0); Assertions.assertEquals("", buffer.toString()); } @Test public void testInvalidAppendAsciiByteArray() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(4); buffer.append((byte[])null, 0, 0); final byte[] tmp = new byte[] { '1', '2', '3', '4'}; Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, -1, 0)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, -1)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, 8)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 10, Integer.MAX_VALUE)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 2, 4)); } @Test public void testSerialization() throws Exception { final CharArrayBuffer orig = new CharArrayBuffer(32); orig.append('a'); orig.append('b'); orig.append('c'); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); try (final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer)) { outStream.writeObject(orig); } final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final CharArrayBuffer clone = (CharArrayBuffer) inStream.readObject(); Assertions.assertEquals(orig.capacity(), clone.capacity()); Assertions.assertEquals(orig.length(), clone.length()); final char[] data = clone.toCharArray(); Assertions.assertNotNull(data); Assertions.assertEquals(3, data.length); Assertions.assertEquals('a', data[0]); Assertions.assertEquals('b', data[1]); Assertions.assertEquals('c', data[2]); } @Test public void testSubSequence() { final CharArrayBuffer buffer = new CharArrayBuffer(16); buffer.append(" name: value "); Assertions.assertEquals(5, buffer.indexOf(':')); Assertions.assertEquals(" name", buffer.subSequence(0, 5).toString()); Assertions.assertEquals(" value ", buffer.subSequence(6, buffer.length()).toString()); } @Test public void testSubSequenceIndexOfOutBound() { final CharArrayBuffer buffer = new CharArrayBuffer(16); buffer.append("stuff"); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.subSequence(-2, 10)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.subSequence(12, 10)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.subSequence(2, 1)); } } httpcore5/src/test/java/org/apache/hc/core5/util/TestLangUtils.java0100664 0000000 0000000 00000004463 14403631147 024226 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link LangUtils}. * */ public class TestLangUtils { @Test public void testBasicHash() { final Integer i = Integer.valueOf(1234); final int h1 = LangUtils.hashCode(LangUtils.HASH_SEED, i.hashCode()); final int h2 = LangUtils.hashCode(LangUtils.HASH_SEED, i); Assertions.assertEquals(h1, h2); } @Test public void testNullObjectHash() { final int h1 = LangUtils.hashCode(LangUtils.HASH_SEED, null); final int h2 = LangUtils.hashCode(LangUtils.HASH_SEED, 0); Assertions.assertEquals(h1, h2); } @Test public void testBooleanHash() { final int h1 = LangUtils.hashCode(LangUtils.HASH_SEED, true); final int h2 = LangUtils.hashCode(LangUtils.HASH_SEED, false); final int h3 = LangUtils.hashCode(LangUtils.HASH_SEED, true); final int h4 = LangUtils.hashCode(LangUtils.HASH_SEED, false); Assertions.assertTrue(h1 != h2); Assertions.assertEquals(h1, h3); Assertions.assertEquals(h2, h4); } } httpcore5/src/test/java/org/apache/hc/core5/util/TestArgs.java0100664 0000000 0000000 00000022125 14403631147 023213 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.text.ParseException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link Args}. */ public class TestArgs { @Test public void testArgCheckPass() { Args.check(true, "All is well"); } @Test public void testArgCheckFail() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.check(false, "Oopsie")); } @Test public void testArgNotNullPass() { final String stuff = "stuff"; Assertions.assertSame(stuff, Args.notNull(stuff, "Stuff")); } @Test public void testArgNotNullFail() { Assertions.assertThrows(NullPointerException.class, () -> Args.notNull(null, "Stuff")); } @Test public void testArgNotEmptyPass() { final String stuff = "stuff"; Assertions.assertSame(stuff, Args.notEmpty(stuff, "Stuff")); } @Test public void testArgNotEmptyFail1() { Assertions.assertThrows(NullPointerException.class, () -> Args.notEmpty((String) null, "Stuff")); } @Test public void testArgNotEmptyFail2() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.notEmpty("", "Stuff")); } @Test public void testArgNotBlankFail1() { Assertions.assertThrows(NullPointerException.class, () -> Args.notBlank((String) null, "Stuff")); } @Test public void testArgNotBlankFail2() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.notBlank("", "Stuff")); } @Test public void testArgNotBlankFail3() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.notBlank(" \t \n\r", "Stuff")); } @Test public void testArgCollectionNotEmptyPass() { final List list = Collections.singletonList("stuff"); Assertions.assertSame(list, Args.notEmpty(list, "List")); } @Test public void testArgCollectionNotEmptyFail1() { Assertions.assertThrows(NullPointerException.class, () -> Args.notEmpty((List) null, "List")); } @Test public void testArgCollectionNotEmptyFail2() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.notEmpty(Collections.emptyList(), "List")); } @Test public void testPositiveIntPass() { Assertions.assertEquals(1, Args.positive(1, "Number")); } @Test public void testPositiveIntFail1() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.positive(-1, "Number")); } @Test public void testPositiveIntFail2() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.positive(0, "Number")); } @Test public void testPositiveLongPass() { Assertions.assertEquals(1L, Args.positive(1L, "Number")); } @Test public void testPositiveTimeValuePass() throws ParseException { final Timeout timeout = Timeout.parse("1200 MILLISECONDS"); Assertions.assertEquals(timeout, Args.positive(timeout, "No Error")); } @Test public void testPositiveLongFail1() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.positive(-1L, "Number")); } @Test public void testPositiveLongFail2() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.positive(0L, "Number")); } @Test public void testNotNegativeIntPass1() { Assertions.assertEquals(1, Args.notNegative(1, "Number")); } @Test public void testNotNegativeIntPass2() { Assertions.assertEquals(0, Args.notNegative(0, "Number")); } @Test public void testNotNegativeIntFail1() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.notNegative(-1, "Number")); } @Test public void testNotNegativeLongPass1() { Assertions.assertEquals(1L, Args.notNegative(1L, "Number")); } @Test public void testNotNegativeLongPass2() { Assertions.assertEquals(0L, Args.notNegative(0L, "Number")); } @Test public void testNotNegativeLongFail1() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.notNegative(-1L, "Number")); } @Test public void testIntSmallestRangeOK() { Args.checkRange(0, 0, 0, "Number"); } @Test public void testIntSmallestRangeFailLow() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.checkRange(-1, 0, 0, "Number")); } @Test public void testIntRangeFailLow() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.checkRange(-101, -100, 100, "Number")); } @Test public void testIntRangeFailHigh() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.checkRange(101, -100, 100, "Number")); } @Test public void testIntSmallestRangeFailHigh() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.checkRange(1, 0, 0, "Number")); } @Test public void testIntFullRangeOK() { Args.checkRange(0, Integer.MIN_VALUE, Integer.MAX_VALUE, "Number"); } @Test public void testLongSmallestRangeOK() { Args.checkRange(0L, 0L, 0L, "Number"); } @Test public void testLongSmallestRangeFailLow() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.checkRange(-1L, 0L, 0L, "Number")); } @Test public void testLongRangeFailLow() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.checkRange(-101L, -100L, 100L, "Number")); } @Test public void testLongRangeFailHigh() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.checkRange(101L, -100L, 100L, "Number")); } @Test public void testLongSmallestRangeFailHigh() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.checkRange(1L, 0L, 0L, "Number")); } @Test public void testLongFullRangeOK() { Args.checkRange(0L, Long.MIN_VALUE, Long.MAX_VALUE, "Number"); } @Test public void testIsEmpty() { final String[] NON_EMPTY_ARRAY = new String[] { "ABG", "NML", }; final List NON_EMPTY_LIST = Arrays.asList(NON_EMPTY_ARRAY); final Set NON_EMPTY_SET = new HashSet<>(NON_EMPTY_LIST); final Map NON_EMPTY_MAP = new HashMap<>(); NON_EMPTY_MAP.put("ABG", "MNL"); Assertions.assertTrue(Args.isEmpty(null)); Assertions.assertTrue(Args.isEmpty("")); Assertions.assertTrue(Args.isEmpty(new int[] {})); Assertions.assertTrue(Args.isEmpty(Collections.emptyList())); Assertions.assertTrue(Args.isEmpty(Collections.emptySet())); Assertions.assertTrue(Args.isEmpty(Collections.emptyMap())); Assertions.assertFalse(Args.isEmpty(" ")); Assertions.assertFalse(Args.isEmpty("ab")); Assertions.assertFalse(Args.isEmpty(NON_EMPTY_ARRAY)); Assertions.assertFalse(Args.isEmpty(NON_EMPTY_LIST)); Assertions.assertFalse(Args.isEmpty(NON_EMPTY_SET)); Assertions.assertFalse(Args.isEmpty(NON_EMPTY_MAP)); } @Test public void testcontainsNoBlanks() { final String stuff = "abg"; Assertions.assertSame(stuff, Args.containsNoBlanks(stuff, "abg")); } @Test public void check() { Assertions.assertThrows(IllegalArgumentException.class, () -> Args.check(false, "Error,", "ABG")); } } httpcore5/src/test/java/org/apache/hc/core5/util/TestTextUtils.java0100664 0000000 0000000 00000004734 14403631147 024272 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link TextUtils}. * */ public class TestTextUtils { @Test public void testTextEmpty() { Assertions.assertTrue(TextUtils.isEmpty(null)); Assertions.assertTrue(TextUtils.isEmpty("")); Assertions.assertFalse(TextUtils.isEmpty("\t")); } @Test public void testTextBlank() { Assertions.assertTrue(TextUtils.isBlank(null)); Assertions.assertTrue(TextUtils.isBlank("")); Assertions.assertTrue(TextUtils.isBlank(" ")); Assertions.assertTrue(TextUtils.isBlank("\t")); } @Test public void testTextContainsBlanks() { Assertions.assertFalse(TextUtils.containsBlanks(null)); Assertions.assertFalse(TextUtils.containsBlanks("")); Assertions.assertTrue(TextUtils.containsBlanks(" ")); Assertions.assertTrue(TextUtils.containsBlanks("\t")); Assertions.assertTrue(TextUtils.containsBlanks(" a")); Assertions.assertFalse(TextUtils.containsBlanks("a")); } @Test public void testToHexString() { Assertions.assertEquals("000c2001ff", TextUtils.toHexString(new byte[] { 0, 12, 32, 1 , -1})); Assertions.assertNull(TextUtils.toHexString(null)); } } httpcore5/src/test/java/org/apache/hc/core5/util/TestByteArrayBuffer.java0100664 0000000 0000000 00000035131 14403631147 025354 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link ByteArrayBuffer}. * */ public class TestByteArrayBuffer { @Test public void testConstructor() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(16); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(0, buffer.length()); Assertions.assertNotNull(buffer.array()); Assertions.assertEquals(16, buffer.array().length); Assertions.assertThrows(IllegalArgumentException.class, () -> new ByteArrayBuffer(-1)); } @Test public void testSimpleAppend() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(16); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(0, buffer.length()); final byte[] b1 = buffer.toByteArray(); Assertions.assertNotNull(b1); Assertions.assertEquals(0, b1.length); Assertions.assertTrue(buffer.isEmpty()); Assertions.assertFalse(buffer.isFull()); final byte[] tmp = new byte[] { 1, 2, 3, 4}; buffer.append(tmp, 0, tmp.length); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(4, buffer.length()); Assertions.assertFalse(buffer.isEmpty()); Assertions.assertFalse(buffer.isFull()); final byte[] b2 = buffer.toByteArray(); Assertions.assertNotNull(b2); Assertions.assertEquals(4, b2.length); for (int i = 0; i < tmp.length; i++) { Assertions.assertEquals(tmp[i], b2[i]); Assertions.assertEquals(tmp[i], buffer.byteAt(i)); } buffer.clear(); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(0, buffer.length()); Assertions.assertTrue(buffer.isEmpty()); Assertions.assertFalse(buffer.isFull()); } @Test public void testExpandAppend() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(4); Assertions.assertEquals(4, buffer.capacity()); final byte[] tmp = new byte[] { 1, 2, 3, 4}; buffer.append(tmp, 0, 2); buffer.append(tmp, 0, 4); buffer.append(tmp, 0, 0); Assertions.assertEquals(8, buffer.capacity()); Assertions.assertEquals(6, buffer.length()); buffer.append(tmp, 0, 4); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(10, buffer.length()); } @Test public void testAppendHeapByteBuffer() { final ByteArrayBuffer buffer = new ByteArrayBuffer(4); Assertions.assertEquals(4, buffer.capacity()); final ByteBuffer tmp = ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5, 6}); buffer.append(tmp); Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained"); Assertions.assertEquals(8, buffer.capacity()); Assertions.assertEquals(6, buffer.length()); tmp.clear(); buffer.append(tmp); Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained"); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(12, buffer.length()); Assertions.assertArrayEquals(new byte[] {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, buffer.toByteArray()); } @Test public void testAppendHeapByteBufferWithOffset() { final ByteArrayBuffer buffer = new ByteArrayBuffer(4); Assertions.assertEquals(4, buffer.capacity()); final ByteBuffer tmp = ByteBuffer.wrap(new byte[] { 7, 7, 1, 2, 3, 4, 5, 6, 7, 7}, 2, 6).slice(); Assertions.assertTrue(tmp.arrayOffset() > 0, "Validate this is testing a buffer with an array offset"); buffer.append(tmp); Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained"); Assertions.assertEquals(8, buffer.capacity()); Assertions.assertEquals(6, buffer.length()); tmp.clear(); Assertions.assertEquals(6, tmp.remaining()); buffer.append(tmp); Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained"); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(12, buffer.length()); Assertions.assertArrayEquals(new byte[] {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, buffer.toByteArray()); } @Test public void testAppendDirectByteBuffer() { final ByteArrayBuffer buffer = new ByteArrayBuffer(4); Assertions.assertEquals(4, buffer.capacity()); final ByteBuffer tmp = ByteBuffer.allocateDirect(6); tmp.put(new byte[] { 1, 2, 3, 4, 5, 6}).flip(); buffer.append(tmp); Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained"); Assertions.assertEquals(8, buffer.capacity()); Assertions.assertEquals(6, buffer.length()); tmp.clear(); buffer.append(tmp); Assertions.assertFalse(tmp.hasRemaining(), "The input buffer should be drained"); Assertions.assertEquals(16, buffer.capacity()); Assertions.assertEquals(12, buffer.length()); Assertions.assertArrayEquals(new byte[] {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, buffer.toByteArray()); } @Test public void testInvalidAppend() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(4); buffer.append((byte[])null, 0, 0); final byte[] tmp = new byte[] { 1, 2, 3, 4}; Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, -1, 0)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, -1)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, 8)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 10, Integer.MAX_VALUE)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 2, 4)); } @Test public void testAppendOneByte() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(4); Assertions.assertEquals(4, buffer.capacity()); final byte[] tmp = new byte[] { 1, 127, -1, -128, 1, -2}; for (final byte element : tmp) { buffer.append(element); } Assertions.assertEquals(8, buffer.capacity()); Assertions.assertEquals(6, buffer.length()); for (int i = 0; i < tmp.length; i++) { Assertions.assertEquals(tmp[i], buffer.byteAt(i)); } } @Test public void testSetLength() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(4); buffer.setLength(2); Assertions.assertEquals(2, buffer.length()); } @Test public void testSetInvalidLength() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(4); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.setLength(-2)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.setLength(200)); } @Test public void testEnsureCapacity() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(4); buffer.ensureCapacity(2); Assertions.assertEquals(4, buffer.capacity()); buffer.ensureCapacity(8); Assertions.assertEquals(8, buffer.capacity()); } @Test public void testIndexOf() throws Exception { final byte COLON = (byte) ':'; final byte COMMA = (byte) ','; final byte[] bytes = "name1: value1; name2: value2".getBytes(StandardCharsets.US_ASCII); final int index1 = 5; final int index2 = 20; final ByteArrayBuffer buffer = new ByteArrayBuffer(16); buffer.append(bytes, 0, bytes.length); Assertions.assertEquals(index1, buffer.indexOf(COLON)); Assertions.assertEquals(-1, buffer.indexOf(COMMA)); Assertions.assertEquals(index1, buffer.indexOf(COLON, -1, 11)); Assertions.assertEquals(index1, buffer.indexOf(COLON, 0, 1000)); Assertions.assertEquals(-1, buffer.indexOf(COLON, 2, 1)); Assertions.assertEquals(index2, buffer.indexOf(COLON, index1 + 1, buffer.length())); } @Test public void testAppendCharArrayAsAscii() throws Exception { final String s1 = "stuff"; final String s2 = " and more stuff"; final char[] b1 = s1.toCharArray(); final char[] b2 = s2.toCharArray(); final ByteArrayBuffer buffer = new ByteArrayBuffer(8); buffer.append(b1, 0, b1.length); buffer.append(b2, 0, b2.length); Assertions.assertEquals(s1 + s2, new String(buffer.toByteArray(), StandardCharsets.US_ASCII)); } @Test public void testAppendNullCharArray() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(8); buffer.append((char[])null, 0, 0); Assertions.assertEquals(0, buffer.length()); } @Test public void testAppendEmptyCharArray() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(8); buffer.append(new char[] {}, 0, 0); Assertions.assertEquals(0, buffer.length()); } @Test public void testAppendNullCharArrayBuffer() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(8); buffer.append((CharArrayBuffer)null, 0, 0); Assertions.assertEquals(0, buffer.length()); } @Test public void testAppendNullByteBuffer() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(8); final ByteBuffer nullBuffer = null; buffer.append(nullBuffer); Assertions.assertEquals(0, buffer.length()); } @Test public void testInvalidAppendCharArrayAsAscii() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(4); buffer.append((char[])null, 0, 0); final char[] tmp = new char[] { '1', '2', '3', '4'}; Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, -1, 0)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, -1)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, 8)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 10, Integer.MAX_VALUE)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 2, 4)); } @Test public void testSerialization() throws Exception { final ByteArrayBuffer orig = new ByteArrayBuffer(32); orig.append(1); orig.append(2); orig.append(3); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); try (final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer)) { outStream.writeObject(orig); } final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final ByteArrayBuffer clone = (ByteArrayBuffer) inStream.readObject(); Assertions.assertEquals(orig.capacity(), clone.capacity()); Assertions.assertEquals(orig.length(), clone.length()); final byte[] data = clone.toByteArray(); Assertions.assertNotNull(data); Assertions.assertEquals(3, data.length); Assertions.assertEquals(1, data[0]); Assertions.assertEquals(2, data[1]); Assertions.assertEquals(3, data[2]); } @Test public void testControlCharFiltering() throws Exception { final char[] chars = new char[256]; for (char i = 0; i < 256; i++) { chars[i] = i; } final byte[] bytes = asByteArray(chars); Assertions.assertEquals( "?????????\t??????????????????????" + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz" + "{|}~???????????????????????" + "??????????\u00A0¡¢£¤¥¦§¨©ª«¬\u00AD®¯" + "°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ" + "ÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîï" + "ðñòóôõö÷øùúûüýþÿ", new String(bytes, StandardCharsets.ISO_8859_1)); } @Test public void testUnicodeFiltering() throws Exception { // Various languages Assertions.assertEquals("?????", new String(asByteArray("буквы".toCharArray()), StandardCharsets.ISO_8859_1)); Assertions.assertEquals("????", new String(asByteArray("四字熟語".toCharArray()), StandardCharsets.ISO_8859_1)); // Unicode snowman Assertions.assertEquals("?", new String(asByteArray("☃".toCharArray()), StandardCharsets.ISO_8859_1)); // Emoji (surrogate pair) Assertions.assertEquals("??", new String(asByteArray("\uD83D\uDE00".toCharArray()), StandardCharsets.ISO_8859_1)); } private static byte[] asByteArray(final char[] chars) { final ByteArrayBuffer byteArrayBuffer = new ByteArrayBuffer(chars.length); byteArrayBuffer.append(chars, 0, chars.length); return byteArrayBuffer.toByteArray(); } } httpcore5/src/test/java/org/apache/hc/core5/util/TestTokenizer.java0100664 0000000 0000000 00000017132 14403631147 024273 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestTokenizer { private Tokenizer parser; @BeforeEach public void setUp() throws Exception { parser = new Tokenizer(); } private static CharArrayBuffer createBuffer(final String value) { if (value == null) { return null; } final CharArrayBuffer buffer = new CharArrayBuffer(value.length()); buffer.append(value); return buffer; } @Test public void testBasicTokenParsing() throws Exception { final String s = " raw: \" some stuff \""; final CharArrayBuffer raw = createBuffer(s); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); parser.skipWhiteSpace(raw, cursor); Assertions.assertFalse(cursor.atEnd()); Assertions.assertEquals(3, cursor.getPos()); final StringBuilder strbuf1 = new StringBuilder(); parser.copyContent(raw, cursor, Tokenizer.INIT_BITSET(':'), strbuf1); Assertions.assertFalse(cursor.atEnd()); Assertions.assertEquals(6, cursor.getPos()); Assertions.assertEquals("raw", strbuf1.toString()); Assertions.assertEquals(':', raw.charAt(cursor.getPos())); cursor.updatePos(cursor.getPos() + 1); parser.skipWhiteSpace(raw, cursor); Assertions.assertFalse(cursor.atEnd()); Assertions.assertEquals(8, cursor.getPos()); final StringBuilder strbuf2 = new StringBuilder(); parser.copyQuotedContent(raw, cursor, strbuf2); Assertions.assertTrue(cursor.atEnd()); Assertions.assertEquals(" some stuff ", strbuf2.toString()); parser.copyQuotedContent(raw, cursor, strbuf2); Assertions.assertTrue(cursor.atEnd()); parser.skipWhiteSpace(raw, cursor); Assertions.assertTrue(cursor.atEnd()); } @Test public void testTokenParsingWithQuotedPairs() throws Exception { final String s = "raw: \"\\\"some\\stuff\\\\\""; final CharArrayBuffer raw = createBuffer(s); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); parser.skipWhiteSpace(raw, cursor); Assertions.assertFalse(cursor.atEnd()); Assertions.assertEquals(0, cursor.getPos()); final StringBuilder strbuf1 = new StringBuilder(); parser.copyContent(raw, cursor, Tokenizer.INIT_BITSET(':'), strbuf1); Assertions.assertFalse(cursor.atEnd()); Assertions.assertEquals("raw", strbuf1.toString()); Assertions.assertEquals(':', raw.charAt(cursor.getPos())); cursor.updatePos(cursor.getPos() + 1); parser.skipWhiteSpace(raw, cursor); Assertions.assertFalse(cursor.atEnd()); final StringBuilder strbuf2 = new StringBuilder(); parser.copyQuotedContent(raw, cursor, strbuf2); Assertions.assertTrue(cursor.atEnd()); Assertions.assertEquals("\"some\\stuff\\", strbuf2.toString()); } @Test public void testTokenParsingIncompleteQuote() throws Exception { final String s = "\"stuff and more stuff "; final CharArrayBuffer raw = createBuffer(s); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); final StringBuilder strbuf1 = new StringBuilder(); parser.copyQuotedContent(raw, cursor, strbuf1); Assertions.assertEquals("stuff and more stuff ", strbuf1.toString()); } @Test public void testTokenParsingTokensWithUnquotedBlanks() throws Exception { final String s = " stuff and \tsome\tmore stuff ;"; final CharArrayBuffer raw = createBuffer(s); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); final String result = parser.parseToken(raw, cursor, Tokenizer.INIT_BITSET(';')); Assertions.assertEquals("stuff and some more stuff", result); } @Test public void testTokenParsingMixedValuesAndQuotedValues() throws Exception { final String s = " stuff and \" some more \" \"stuff ;"; final CharArrayBuffer raw = createBuffer(s); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); final String result = parser.parseValue(raw, cursor, Tokenizer.INIT_BITSET(';')); Assertions.assertEquals("stuff and some more stuff ;", result); } @Test public void testTokenParsingMixedValuesAndQuotedValues2() throws Exception { final String s = "stuff\"more\"stuff;"; final CharArrayBuffer raw = createBuffer(s); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); final String result = parser.parseValue(raw, cursor, Tokenizer.INIT_BITSET(';')); Assertions.assertEquals("stuffmorestuff", result); } @Test public void testTokenParsingEscapedQuotes() throws Exception { final String s = "stuff\"\\\"more\\\"\"stuff;"; final CharArrayBuffer raw = createBuffer(s); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); final String result = parser.parseValue(raw, cursor, Tokenizer.INIT_BITSET(';')); Assertions.assertEquals("stuff\"more\"stuff", result); } @Test public void testTokenParsingEscapedDelimiter() throws Exception { final String s = "stuff\"\\\"more\\\";\"stuff;"; final CharArrayBuffer raw = createBuffer(s); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); final String result = parser.parseValue(raw, cursor, Tokenizer.INIT_BITSET(';')); Assertions.assertEquals("stuff\"more\";stuff", result); } @Test public void testTokenParsingEscapedSlash() throws Exception { final String s = "stuff\"\\\"more\\\";\\\\\"stuff;"; final CharArrayBuffer raw = createBuffer(s); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); final String result = parser.parseValue(raw, cursor, Tokenizer.INIT_BITSET(';')); Assertions.assertEquals("stuff\"more\";\\stuff", result); } @Test public void testTokenParsingSlashOutsideQuotes() throws Exception { final String s = "stuff\\; more stuff;"; final CharArrayBuffer raw = createBuffer(s); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); final String result = parser.parseValue(raw, cursor, Tokenizer.INIT_BITSET(';')); Assertions.assertEquals("stuff\\", result); } } httpcore5/src/test/java/org/apache/hc/core5/util/TestTimeValue.java0100664 0000000 0000000 00000034750 14403631147 024221 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import static org.hamcrest.MatcherAssert.assertThat; import java.text.ParseException; import java.time.Duration; import java.util.concurrent.TimeUnit; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestTimeValue { private void checkToDays(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toDays(value), TimeValue.of(value, timeUnit).toDays()); } private void checkToHours(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toHours(value), TimeValue.of(value, timeUnit).toHours()); } private void checkToMicroseconds(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toMicros(value), TimeValue.of(value, timeUnit).toMicroseconds()); } private void checkToMilliseconds(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toMillis(value), TimeValue.of(value, timeUnit).toMilliseconds()); } private void checkToMinutes(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toMinutes(value), TimeValue.of(value, timeUnit).toMinutes()); } private void checkToNanoseconds(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toNanos(value), TimeValue.of(value, timeUnit).toNanoseconds()); } private void checkToSeconds(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toSeconds(value), TimeValue.of(value, timeUnit).toSeconds()); } private void test(final long value) { for (final TimeUnit timeUnit : TimeUnit.values()) { checkToDays(value, timeUnit); checkToHours(value, timeUnit); checkToMinutes(value, timeUnit); checkToSeconds(value, timeUnit); checkToMilliseconds(value, timeUnit); checkToMicroseconds(value, timeUnit); checkToNanoseconds(value, timeUnit); } } @Test public void test0() { test(0); } @Test public void test1() { test(1); } @Test public void testConvert() { Assertions.assertEquals(0, TimeValue.ofMilliseconds(0).convert(TimeUnit.DAYS)); Assertions.assertEquals(1000, TimeValue.ofSeconds(1).convert(TimeUnit.MILLISECONDS)); } @Test public void testDivide() { // nominator is 0, result should be 0. Assertions.assertEquals(0, TimeValue.ofMilliseconds(0).divide(2).toDays()); Assertions.assertEquals(0, TimeValue.ofMilliseconds(0).divide(2).toHours()); Assertions.assertEquals(0, TimeValue.ofMilliseconds(0).divide(2).toMicroseconds()); Assertions.assertEquals(0, TimeValue.ofMilliseconds(0).divide(2).toMilliseconds()); Assertions.assertEquals(0, TimeValue.ofMilliseconds(0).divide(2).toMinutes()); Assertions.assertEquals(0, TimeValue.ofMilliseconds(0).divide(2).toNanoseconds()); Assertions.assertEquals(0, TimeValue.ofMilliseconds(0).divide(2).toSeconds()); Assertions.assertEquals(0, TimeValue.ofMilliseconds(0).divide(2).toMillisecondsIntBound()); Assertions.assertEquals(0, TimeValue.ofMilliseconds(0).divide(2).toSecondsIntBound()); // Assertions.assertEquals(50, TimeValue.ofMilliseconds(100).divide(2).toMilliseconds()); Assertions.assertEquals(0, TimeValue.ofMinutes(1).divide(2).toSeconds()); Assertions.assertEquals(30, TimeValue.ofMinutes(1).divide(2, TimeUnit.SECONDS).toSeconds()); Assertions.assertEquals(30000, TimeValue.ofMinutes(1).divide(2, TimeUnit.MILLISECONDS).toMilliseconds()); } @Test public void testDivideBy0() { Assertions.assertThrows(ArithmeticException.class, () -> TimeValue.ofMilliseconds(0).divide(0)); } private void testFactory(final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit, TimeValue.of(1, timeUnit).getTimeUnit()); // final Duration duration = Duration.of(1, TimeValue.toChronoUnit(timeUnit)); assertConvertion(duration); } @Test public void testFactoryForDays() { testFactory(TimeUnit.DAYS); } @Test public void testFactoryForDuration() { assertConvertion(Duration.ZERO); assertConvertion(Duration.ofDays(1)); assertConvertion(Duration.ofHours(1)); assertConvertion(Duration.ofMillis(1)); assertConvertion(Duration.ofNanos(1)); assertConvertion(Duration.ofSeconds(1)); assertConvertion(Duration.ofSeconds(1, 1)); } private void assertConvertion(final Duration duration) { Assertions.assertEquals(duration, TimeValue.of(duration).toDuration()); } @Test public void testFactoryForHours() { testFactory(TimeUnit.HOURS); } @Test public void testFactoryForMicroseconds() { testFactory(TimeUnit.MICROSECONDS); } @Test public void testFactoryForMilliseconds() { testFactory(TimeUnit.MILLISECONDS); } @Test public void testFactoryForMinutes() { testFactory(TimeUnit.MINUTES); } @Test public void testFactoryForNanoseconds() { testFactory(TimeUnit.NANOSECONDS); } @Test public void testFactoryForSeconds() { testFactory(TimeUnit.SECONDS); } @Test public void testMin() { final TimeValue nanos1 = TimeValue.ofNanoseconds(1); final TimeValue micros1 = TimeValue.ofMicroseconds(1); final TimeValue millis1 = TimeValue.ofMilliseconds(1); final TimeValue seconds1 = TimeValue.ofSeconds(1); final TimeValue minutes1 = TimeValue.ofMinutes(1); final TimeValue hours1 = TimeValue.ofHours(1); final TimeValue days1 = TimeValue.ofDays(1); // Assertions.assertEquals(TimeValue.ZERO_MILLISECONDS, TimeValue.ZERO_MILLISECONDS.min(nanos1)); Assertions.assertEquals(TimeValue.ZERO_MILLISECONDS, TimeValue.ZERO_MILLISECONDS.min(micros1)); Assertions.assertEquals(TimeValue.ZERO_MILLISECONDS, TimeValue.ZERO_MILLISECONDS.min(millis1)); Assertions.assertEquals(TimeValue.ZERO_MILLISECONDS, TimeValue.ZERO_MILLISECONDS.min(seconds1)); Assertions.assertEquals(TimeValue.ZERO_MILLISECONDS, TimeValue.ZERO_MILLISECONDS.min(minutes1)); Assertions.assertEquals(TimeValue.ZERO_MILLISECONDS, TimeValue.ZERO_MILLISECONDS.min(hours1)); Assertions.assertEquals(TimeValue.ZERO_MILLISECONDS, TimeValue.ZERO_MILLISECONDS.min(days1)); // Assertions.assertEquals(nanos1, nanos1.min(nanos1)); Assertions.assertEquals(nanos1, nanos1.min(micros1)); Assertions.assertEquals(nanos1, nanos1.min(millis1)); Assertions.assertEquals(nanos1, nanos1.min(seconds1)); Assertions.assertEquals(nanos1, nanos1.min(minutes1)); Assertions.assertEquals(nanos1, nanos1.min(hours1)); Assertions.assertEquals(nanos1, nanos1.min(days1)); // Assertions.assertEquals(nanos1, micros1.min(nanos1)); Assertions.assertEquals(micros1, micros1.min(micros1)); Assertions.assertEquals(micros1, micros1.min(millis1)); Assertions.assertEquals(micros1, micros1.min(seconds1)); Assertions.assertEquals(micros1, micros1.min(minutes1)); Assertions.assertEquals(micros1, micros1.min(hours1)); Assertions.assertEquals(micros1, micros1.min(days1)); // Assertions.assertEquals(nanos1, millis1.min(nanos1)); Assertions.assertEquals(micros1, millis1.min(micros1)); Assertions.assertEquals(millis1, millis1.min(millis1)); Assertions.assertEquals(millis1, millis1.min(seconds1)); Assertions.assertEquals(millis1, millis1.min(minutes1)); Assertions.assertEquals(millis1, millis1.min(hours1)); Assertions.assertEquals(millis1, millis1.min(days1)); // Assertions.assertEquals(nanos1, seconds1.min(nanos1)); Assertions.assertEquals(micros1, seconds1.min(micros1)); Assertions.assertEquals(millis1, seconds1.min(millis1)); Assertions.assertEquals(seconds1, seconds1.min(seconds1)); Assertions.assertEquals(seconds1, seconds1.min(minutes1)); Assertions.assertEquals(seconds1, seconds1.min(hours1)); Assertions.assertEquals(seconds1, seconds1.min(days1)); // Assertions.assertEquals(nanos1, minutes1.min(nanos1)); Assertions.assertEquals(micros1, minutes1.min(micros1)); Assertions.assertEquals(millis1, minutes1.min(millis1)); Assertions.assertEquals(seconds1, minutes1.min(seconds1)); Assertions.assertEquals(minutes1, minutes1.min(minutes1)); Assertions.assertEquals(minutes1, minutes1.min(hours1)); Assertions.assertEquals(minutes1, minutes1.min(days1)); // Assertions.assertEquals(nanos1, hours1.min(nanos1)); Assertions.assertEquals(micros1, hours1.min(micros1)); Assertions.assertEquals(millis1, hours1.min(millis1)); Assertions.assertEquals(seconds1, hours1.min(seconds1)); Assertions.assertEquals(minutes1, hours1.min(minutes1)); Assertions.assertEquals(hours1, hours1.min(hours1)); Assertions.assertEquals(hours1, hours1.min(days1)); // Assertions.assertEquals(nanos1, days1.min(nanos1)); Assertions.assertEquals(micros1, days1.min(micros1)); Assertions.assertEquals(millis1, days1.min(millis1)); Assertions.assertEquals(seconds1, days1.min(seconds1)); Assertions.assertEquals(minutes1, days1.min(minutes1)); Assertions.assertEquals(hours1, days1.min(hours1)); Assertions.assertEquals(days1, days1.min(days1)); } @Test public void testMaxInt() { test(Integer.MAX_VALUE); } @Test public void testMaxLong() { test(Long.MAX_VALUE); } @Test public void testNegative1() { test(-1); } @Test public void testToString() { Assertions.assertEquals("9223372036854775807 SECONDS", TimeValue.ofSeconds(Long.MAX_VALUE).toString()); Assertions.assertEquals("0 MILLISECONDS", TimeValue.ZERO_MILLISECONDS.toString()); } @Test public void testFromString() throws ParseException { final TimeValue maxSeconds = TimeValue.ofSeconds(Long.MAX_VALUE); Assertions.assertEquals(maxSeconds, TimeValue.parse("9223372036854775807 SECONDS")); Assertions.assertEquals(maxSeconds, TimeValue.parse("9223372036854775807 SECONDS")); Assertions.assertEquals(maxSeconds, TimeValue.parse(" 9223372036854775807 SECONDS ")); Assertions.assertEquals(maxSeconds, TimeValue.parse("9223372036854775807 Seconds")); Assertions.assertEquals(maxSeconds, TimeValue.parse("9223372036854775807 Seconds")); Assertions.assertEquals(maxSeconds, TimeValue.parse("9223372036854775807\tSeconds")); Assertions.assertEquals(TimeValue.ZERO_MILLISECONDS, TimeValue.parse("0 MILLISECONDS")); Assertions.assertEquals(TimeValue.ofMilliseconds(1), TimeValue.parse("1 MILLISECOND")); } @Test public void testToDuration() throws ParseException { Assertions.assertEquals(Long.MAX_VALUE, TimeValue.parse("9223372036854775807 SECONDS").toDuration().getSeconds()); } @Test public void testEqualsAndHashCode() { final TimeValue tv1 = TimeValue.ofMilliseconds(1000L); final TimeValue tv2 = TimeValue.ofMilliseconds(1001L); final TimeValue tv3 = TimeValue.ofMilliseconds(1000L); final TimeValue tv4 = TimeValue.ofSeconds(1L); final TimeValue tv5 = TimeValue.ofSeconds(1000L); assertThat(tv1.equals(tv1), CoreMatchers.equalTo(true)); assertThat(tv1.equals(null), CoreMatchers.equalTo(false)); assertThat(tv1.equals(tv2), CoreMatchers.equalTo(false)); assertThat(tv1.equals(tv3), CoreMatchers.equalTo(true)); assertThat(tv1.equals(tv4), CoreMatchers.equalTo(true)); assertThat(tv4.equals(tv1), CoreMatchers.equalTo(true)); assertThat(tv1.equals(tv5), CoreMatchers.equalTo(false)); assertThat(tv1.hashCode() == tv2.hashCode(), CoreMatchers.equalTo(false)); assertThat(tv1.hashCode() == tv3.hashCode(), CoreMatchers.equalTo(true)); assertThat(tv1.hashCode() == tv4.hashCode(), CoreMatchers.equalTo(true)); assertThat(tv4.hashCode() == tv1.hashCode(), CoreMatchers.equalTo(true)); assertThat(tv1.hashCode() == tv5.hashCode(), CoreMatchers.equalTo(false)); } @Test public void testCompareTo() { final TimeValue tv1 = TimeValue.ofMilliseconds(1000L); final TimeValue tv2 = TimeValue.ofMilliseconds(1001L); final TimeValue tv3 = TimeValue.ofMilliseconds(1000L); final TimeValue tv4 = TimeValue.ofSeconds(1L); final TimeValue tv5 = TimeValue.ofSeconds(60L); final TimeValue tv6 = TimeValue.ofMinutes(1L); assertThat(tv1.compareTo(tv1) == 0, CoreMatchers.equalTo(true)); assertThat(tv1.compareTo(tv2) < 0, CoreMatchers.equalTo(true)); assertThat(tv1.compareTo(tv3) == 0, CoreMatchers.equalTo(true)); assertThat(tv1.compareTo(tv4) == 0, CoreMatchers.equalTo(true)); assertThat(tv1.compareTo(tv5) < 0, CoreMatchers.equalTo(true)); assertThat(tv6.compareTo(tv5) == 0, CoreMatchers.equalTo(true)); assertThat(tv6.compareTo(tv4) > 0, CoreMatchers.equalTo(true)); Assertions.assertThrows(NullPointerException.class, () -> tv1.compareTo(null)); } } httpcore5/src/test/java/org/apache/hc/core5/util/TestTimeout.java0100664 0000000 0000000 00000013623 14403631147 023750 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.text.ParseException; import java.time.Duration; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestTimeout { private void checkToDays(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toDays(value), Timeout.of(value, timeUnit).toDays()); } private void checkToHours(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toHours(value), Timeout.of(value, timeUnit).toHours()); } private void checkToMicroseconds(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toMicros(value), Timeout.of(value, timeUnit).toMicroseconds()); } private void checkToMilliseconds(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toMillis(value), Timeout.of(value, timeUnit).toMilliseconds()); } private void checkToMinutes(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toMinutes(value), Timeout.of(value, timeUnit).toMinutes()); } private void checkToNanoseconds(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toNanos(value), Timeout.of(value, timeUnit).toNanoseconds()); } private void checkToSeconds(final long value, final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit.toSeconds(value), Timeout.of(value, timeUnit).toSeconds()); } private void test(final long value) { for (final TimeUnit timeUnit : TimeUnit.values()) { checkToDays(value, timeUnit); checkToHours(value, timeUnit); checkToMinutes(value, timeUnit); checkToSeconds(value, timeUnit); checkToMilliseconds(value, timeUnit); checkToMicroseconds(value, timeUnit); checkToNanoseconds(value, timeUnit); } } @Test public void test0() { test(0); } @Test public void test1() { test(1); } @Test public void testDisabled() { Assertions.assertTrue(Timeout.DISABLED.isDisabled()); Assertions.assertFalse(Timeout.DISABLED.isEnabled()); } private void testFactory(final TimeUnit timeUnit) { Assertions.assertEquals(timeUnit, Timeout.of(1, timeUnit).getTimeUnit()); } @Test public void testFactoryForDays() { testFactory(TimeUnit.DAYS); } @Test public void testFactoryForDuration() { assertConvertion(Duration.ZERO); assertConvertion(Duration.ofDays(1)); assertConvertion(Duration.ofHours(1)); assertConvertion(Duration.ofMillis(1)); assertConvertion(Duration.ofNanos(1)); assertConvertion(Duration.ofSeconds(1)); assertConvertion(Duration.ofSeconds(1, 1)); } private void assertConvertion(final Duration duration) { Assertions.assertEquals(duration, Timeout.of(duration).toDuration()); } @Test public void testFactoryForHours() { testFactory(TimeUnit.HOURS); } @Test public void testFactoryForMicroseconds() { testFactory(TimeUnit.MICROSECONDS); } @Test public void testFactoryForMillisseconds() { testFactory(TimeUnit.MILLISECONDS); } @Test public void testFactoryForMinutes() { testFactory(TimeUnit.MINUTES); } @Test public void testFactoryForNanoseconds() { testFactory(TimeUnit.NANOSECONDS); } @Test public void testFactoryForSeconds() { testFactory(TimeUnit.SECONDS); } @Test public void testMaxInt() { test(Integer.MAX_VALUE); } @Test public void testMaxLong() { test(Long.MAX_VALUE); } @Test public void testNegative1() { Assertions.assertThrows(IllegalArgumentException.class, () -> test(-1)); } @Test public void testToString() { Assertions.assertEquals("9223372036854775807 SECONDS", Timeout.ofSeconds(Long.MAX_VALUE).toString()); Assertions.assertEquals("0 MILLISECONDS", Timeout.ZERO_MILLISECONDS.toString()); } @Test public void testFromString() throws ParseException { Assertions.assertEquals(Timeout.ofSeconds(Long.MAX_VALUE), Timeout.parse("9223372036854775807 SECONDS")); Assertions.assertEquals(Timeout.ofSeconds(Long.MAX_VALUE), Timeout.parse("9223372036854775807 Seconds")); Assertions.assertEquals(Timeout.ofSeconds(Long.MAX_VALUE), Timeout.parse("9223372036854775807 Seconds")); Assertions.assertEquals(Timeout.ofSeconds(Long.MAX_VALUE), Timeout.parse("9223372036854775807\tSeconds")); Assertions.assertEquals(Timeout.ZERO_MILLISECONDS, Timeout.parse("0 MILLISECONDS")); } } httpcore5/src/test/java/org/apache/hc/core5/util/TestDeadline.java0100664 0000000 0000000 00000012615 14403631147 024027 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import java.text.ParseException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests {@link Deadline}. */ public class TestDeadline { @Test public void testFormat() throws ParseException { final Deadline deadline = Deadline.fromUnixMilliseconds(1000); final Deadline deadline2 = Deadline.parse(deadline.toString()); Assertions.assertEquals(1000, deadline2.getValue()); } @Test public void testIsBefore() { final long nowPlusOneMin = System.currentTimeMillis() + 60000; final Deadline deadline = Deadline.fromUnixMilliseconds(nowPlusOneMin); Assertions.assertTrue(deadline.isBefore(nowPlusOneMin + 1)); } @Test public void testIsExpired() { Assertions.assertTrue(Deadline.fromUnixMilliseconds(0).isExpired()); Assertions.assertTrue(Deadline.fromUnixMilliseconds(1).isExpired()); Assertions.assertFalse(Deadline.MAX_VALUE.isExpired()); Assertions.assertTrue(Deadline.MIN_VALUE.isExpired()); } @Test public void testIsMax() { Assertions.assertFalse(Deadline.fromUnixMilliseconds(0).isMax()); Assertions.assertFalse(Deadline.fromUnixMilliseconds(1000).isMax()); Assertions.assertFalse(Deadline.MIN_VALUE.isMax()); Assertions.assertTrue(Deadline.MAX_VALUE.isMax()); } @Test public void testIsMin() { Assertions.assertTrue(Deadline.fromUnixMilliseconds(0).isMin()); Assertions.assertFalse(Deadline.fromUnixMilliseconds(1000).isMin()); Assertions.assertFalse(Deadline.MAX_VALUE.isMin()); Assertions.assertTrue(Deadline.MIN_VALUE.isMin()); } @Test public void testIsNotExpired() { Assertions.assertFalse(Deadline.fromUnixMilliseconds(0).isNotExpired()); Assertions.assertFalse(Deadline.fromUnixMilliseconds(1).isNotExpired()); Assertions.assertTrue(Deadline.MAX_VALUE.isNotExpired()); Assertions.assertFalse(Deadline.MIN_VALUE.isNotExpired()); } @Test public void testMin() { Assertions.assertEquals(Deadline.MIN_VALUE, Deadline.MIN_VALUE.min(Deadline.MAX_VALUE)); Assertions.assertEquals(Deadline.MIN_VALUE, Deadline.MAX_VALUE.min(Deadline.MIN_VALUE)); // final Deadline deadline0 = Deadline.fromUnixMilliseconds(0); Assertions.assertEquals(Deadline.MIN_VALUE, deadline0.min(Deadline.MIN_VALUE)); Assertions.assertEquals(deadline0, deadline0.min(Deadline.MAX_VALUE)); // final Deadline deadline1 = Deadline.fromUnixMilliseconds(0); Assertions.assertEquals(Deadline.MIN_VALUE, deadline1.min(Deadline.MIN_VALUE)); Assertions.assertEquals(deadline0, deadline1.min(Deadline.MAX_VALUE)); } @Test public void testParse() throws ParseException { final Deadline deadline = Deadline.parse("1969-12-31T17:00:01.000-0700"); Assertions.assertEquals(1000, deadline.getValue()); } @Test public void testRemaining() { final int oneHourInMillis = 60_000 * 60; final long nowPlusOneHour = System.currentTimeMillis() + oneHourInMillis; final Deadline deadline = Deadline.fromUnixMilliseconds(nowPlusOneHour); Assertions.assertEquals(nowPlusOneHour, deadline.getValue()); Assertions.assertTrue(deadline.remaining() > 0); Assertions.assertTrue(deadline.remaining() <= oneHourInMillis); } @Test public void testRemainingTimeValue() { final int oneHourInMillis = 60_000 * 60; final long nowPlusOneHour = System.currentTimeMillis() + oneHourInMillis; final Deadline deadline = Deadline.fromUnixMilliseconds(nowPlusOneHour); Assertions.assertEquals(nowPlusOneHour, deadline.getValue()); Assertions.assertTrue(deadline.remainingTimeValue().toNanoseconds() > 0); Assertions.assertTrue(deadline.remainingTimeValue().toMicroseconds() > 0); Assertions.assertTrue(deadline.remainingTimeValue().toMilliseconds() > 0); } @Test public void testValue() { final long nowPlusOneMin = System.currentTimeMillis() + 60000; final Deadline deadline = Deadline.fromUnixMilliseconds(nowPlusOneMin); Assertions.assertEquals(nowPlusOneMin, deadline.getValue()); } } httpcore5/src/test/java/org/apache/hc/core5/util/TestAsserts.java0100664 0000000 0000000 00000005271 14403631147 023746 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link Asserts}. */ public class TestAsserts { @Test public void testExpressionCheckPass() { Asserts.check(true, "All is well"); } @Test public void testExpressionCheckFail() { Assertions.assertThrows(IllegalStateException.class, () -> Asserts.check(false, "Oopsie")); } @Test public void testExpressionNotNullFail() { Assertions.assertThrows(IllegalStateException.class, () -> Asserts.notNull(null, "Stuff")); } @Test public void testExpressionNotEmptyFail1() { Assertions.assertThrows(IllegalStateException.class, () -> Asserts.notEmpty(null, "Stuff")); } @Test public void testExpressionNotEmptyFail2() { Assertions.assertThrows(IllegalStateException.class, () -> Asserts.notEmpty("", "Stuff")); } @Test public void testExpressionNotEmptyBlank1() { Assertions.assertThrows(IllegalStateException.class, () -> Asserts.notBlank(null, "Stuff")); } @Test public void testExpressionNotEmptyBlank2() { Assertions.assertThrows(IllegalStateException.class, () -> Asserts.notBlank("", "Stuff")); } @Test public void testExpressionNotBlankFail3() { Assertions.assertThrows(IllegalStateException.class, () -> Asserts.notBlank(" \t \n\r", "Stuff")); } } httpcore5/src/test/java/org/apache/hc/core5/util/TestTimeoutValueException.java0100664 0000000 0000000 00000003041 14403631147 026615 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.util; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestTimeoutValueException { @Test public void testMessage() { Assertions.assertEquals("Timeout deadline: 1000 MILLISECONDS, actual: 2000 MILLISECONDS", TimeoutValueException.fromMilliseconds(1000, 2000).getMessage()); } } httpcore5/src/test/java/org/apache/hc/core5/pool/0040775 0000000 0000000 00000000000 14403631147 020611 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java0100664 0000000 0000000 00000070766 14403631147 025251 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import java.util.Collections; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.DeadlineTimeoutException; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; public class TestStrictConnPool { @Test public void testEmptyPool() throws Exception { try (final StrictConnPool pool = new StrictConnPool<>(2, 10)) { final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(0, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); Assertions.assertEquals(10, totals.getMax()); Assertions.assertEquals(Collections.emptySet(), pool.getRoutes()); final PoolStats stats = pool.getStats("somehost"); Assertions.assertEquals(0, stats.getAvailable()); Assertions.assertEquals(0, stats.getLeased()); Assertions.assertEquals(0, stats.getPending()); Assertions.assertEquals(2, stats.getMax()); Assertions.assertEquals("[leased: 0][available: 0][pending: 0]", pool.toString()); } } @Test public void testInvalidConstruction() throws Exception { Assertions.assertThrows(IllegalArgumentException.class, () -> new StrictConnPool(-1, 1)); Assertions.assertThrows(IllegalArgumentException.class, () -> new StrictConnPool(1, -1)); } @Test public void testLeaseRelease() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); final HttpConnection conn2 = Mockito.mock(HttpConnection.class); final HttpConnection conn3 = Mockito.mock(HttpConnection.class); try (final StrictConnPool pool = new StrictConnPool<>(2, 10)) { final Future> future1 = pool.lease("somehost", null); final Future> future2 = pool.lease("somehost", null); final Future> future3 = pool.lease("otherhost", null); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); final PoolEntry entry2 = future2.get(); Assertions.assertNotNull(entry2); entry2.assignConnection(conn2); final PoolEntry entry3 = future3.get(); Assertions.assertNotNull(entry3); entry3.assignConnection(conn3); pool.release(entry1, true); pool.release(entry2, true); pool.release(entry3, false); Mockito.verify(conn1, Mockito.never()).close(ArgumentMatchers.any()); Mockito.verify(conn2, Mockito.never()).close(ArgumentMatchers.any()); Mockito.verify(conn3, Mockito.times(1)).close(CloseMode.GRACEFUL); final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(2, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); } } @Test public void testLeaseInvalid() throws Exception { try (final StrictConnPool pool = new StrictConnPool<>(2, 10)) { Assertions.assertThrows(NullPointerException.class, () -> pool.lease(null, null, Timeout.ZERO_MILLISECONDS, null)); Assertions.assertThrows(NullPointerException.class, () -> pool.lease("somehost", null, null, null)); } } @Test public void testReleaseUnknownEntry() throws Exception { try (final StrictConnPool pool = new StrictConnPool<>(2, 2)) { Assertions.assertThrows(IllegalStateException.class, () -> pool.release(new PoolEntry<>("somehost"), true)); } } @Test public void testMaxLimits() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); final HttpConnection conn2 = Mockito.mock(HttpConnection.class); final HttpConnection conn3 = Mockito.mock(HttpConnection.class); try (final StrictConnPool pool = new StrictConnPool<>(2, 10)) { pool.setMaxPerRoute("somehost", 2); pool.setMaxPerRoute("otherhost", 1); pool.setMaxTotal(3); final Future> future1 = pool.lease("somehost", null); final Future> future2 = pool.lease("somehost", null); final Future> future3 = pool.lease("otherhost", null); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); final PoolEntry entry2 = future2.get(); Assertions.assertNotNull(entry2); entry2.assignConnection(conn2); final PoolEntry entry3 = future3.get(); Assertions.assertNotNull(entry3); entry3.assignConnection(conn3); pool.release(entry1, true); pool.release(entry2, true); pool.release(entry3, true); final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(3, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); final Future> future4 = pool.lease("somehost", null); final Future> future5 = pool.lease("somehost", null); final Future> future6 = pool.lease("otherhost", null); final Future> future7 = pool.lease("somehost", null); final Future> future8 = pool.lease("somehost", null); final Future> future9 = pool.lease("otherhost", null); Assertions.assertTrue(future4.isDone()); final PoolEntry entry4 = future4.get(); Assertions.assertNotNull(entry4); Assertions.assertSame(conn2, entry4.getConnection()); Assertions.assertTrue(future5.isDone()); final PoolEntry entry5 = future5.get(); Assertions.assertNotNull(entry5); Assertions.assertSame(conn1, entry5.getConnection()); Assertions.assertTrue(future6.isDone()); final PoolEntry entry6 = future6.get(); Assertions.assertNotNull(entry6); Assertions.assertSame(conn3, entry6.getConnection()); Assertions.assertFalse(future7.isDone()); Assertions.assertFalse(future8.isDone()); Assertions.assertFalse(future9.isDone()); pool.release(entry4, true); pool.release(entry5, false); pool.release(entry6, true); Assertions.assertTrue(future7.isDone()); final PoolEntry entry7 = future7.get(); Assertions.assertNotNull(entry7); Assertions.assertSame(conn2, entry7.getConnection()); Assertions.assertTrue(future8.isDone()); final PoolEntry entry8 = future8.get(); Assertions.assertNotNull(entry8); Assertions.assertNull(entry8.getConnection()); Assertions.assertTrue(future9.isDone()); final PoolEntry entry9 = future9.get(); Assertions.assertNotNull(entry9); Assertions.assertSame(conn3, entry9.getConnection()); } } @Test public void testConnectionRedistributionOnTotalMaxLimit() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); final HttpConnection conn2 = Mockito.mock(HttpConnection.class); final HttpConnection conn3 = Mockito.mock(HttpConnection.class); final HttpConnection conn4 = Mockito.mock(HttpConnection.class); final HttpConnection conn5 = Mockito.mock(HttpConnection.class); try (final StrictConnPool pool = new StrictConnPool<>(2, 10)) { pool.setMaxPerRoute("somehost", 2); pool.setMaxPerRoute("otherhost", 2); pool.setMaxTotal(2); final Future> future1 = pool.lease("somehost", null); final Future> future2 = pool.lease("somehost", null); final Future> future3 = pool.lease("otherhost", null); final Future> future4 = pool.lease("otherhost", null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); Assertions.assertFalse(entry1.hasConnection()); entry1.assignConnection(conn1); Assertions.assertTrue(future2.isDone()); final PoolEntry entry2 = future2.get(); Assertions.assertNotNull(entry2); Assertions.assertFalse(entry2.hasConnection()); entry2.assignConnection(conn2); Assertions.assertFalse(future3.isDone()); Assertions.assertFalse(future4.isDone()); PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(0, totals.getAvailable()); Assertions.assertEquals(2, totals.getLeased()); Assertions.assertEquals(2, totals.getPending()); pool.release(entry1, true); pool.release(entry2, true); Assertions.assertTrue(future3.isDone()); final PoolEntry entry3 = future3.get(); Assertions.assertNotNull(entry3); Assertions.assertFalse(entry3.hasConnection()); entry3.assignConnection(conn3); Assertions.assertTrue(future4.isDone()); final PoolEntry entry4 = future4.get(); Assertions.assertNotNull(entry4); Assertions.assertFalse(entry4.hasConnection()); entry4.assignConnection(conn4); totals = pool.getTotalStats(); Assertions.assertEquals(0, totals.getAvailable()); Assertions.assertEquals(2, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); final Future> future5 = pool.lease("somehost", null); final Future> future6 = pool.lease("otherhost", null); pool.release(entry3, true); pool.release(entry4, true); Assertions.assertTrue(future5.isDone()); final PoolEntry entry5 = future5.get(); Assertions.assertNotNull(entry5); Assertions.assertFalse(entry5.hasConnection()); entry5.assignConnection(conn5); Assertions.assertTrue(future6.isDone()); final PoolEntry entry6 = future6.get(); Assertions.assertNotNull(entry6); Assertions.assertTrue(entry6.hasConnection()); Assertions.assertSame(conn4, entry6.getConnection()); totals = pool.getTotalStats(); Assertions.assertEquals(0, totals.getAvailable()); Assertions.assertEquals(2, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); pool.release(entry5, true); pool.release(entry6, true); totals = pool.getTotalStats(); Assertions.assertEquals(2, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending());} } @Test public void testStatefulConnectionRedistributionOnPerRouteMaxLimit() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); final HttpConnection conn2 = Mockito.mock(HttpConnection.class); try (final StrictConnPool pool = new StrictConnPool<>(2, 10)) { pool.setMaxPerRoute("somehost", 2); pool.setMaxTotal(2); final Future> future1 = pool.lease("somehost", null); final Future> future2 = pool.lease("somehost", null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); entry1.assignConnection(conn1); Assertions.assertNotNull(entry1); Assertions.assertTrue(future2.isDone()); final PoolEntry entry2 = future2.get(); Assertions.assertNotNull(entry2); entry2.assignConnection(conn2); PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(0, totals.getAvailable()); Assertions.assertEquals(2, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); entry1.updateState("some-stuff"); pool.release(entry1, true); entry2.updateState("some-stuff"); pool.release(entry2, true); final Future> future3 = pool.lease("somehost", "some-stuff"); final Future> future4 = pool.lease("somehost", "some-stuff"); Assertions.assertTrue(future1.isDone()); final PoolEntry entry3 = future3.get(); Assertions.assertNotNull(entry3); Assertions.assertSame(conn2, entry3.getConnection()); Assertions.assertTrue(future4.isDone()); final PoolEntry entry4 = future4.get(); Assertions.assertNotNull(entry4); Assertions.assertSame(conn1, entry4.getConnection()); pool.release(entry3, true); pool.release(entry4, true); totals = pool.getTotalStats(); Assertions.assertEquals(2, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); final Future> future5 = pool.lease("somehost", "some-other-stuff"); Assertions.assertTrue(future5.isDone()); Mockito.verify(conn2).close(CloseMode.GRACEFUL); Mockito.verify(conn1, Mockito.never()).close(ArgumentMatchers.any()); totals = pool.getTotalStats(); Assertions.assertEquals(1, totals.getAvailable()); Assertions.assertEquals(1, totals.getLeased()); } } @Test public void testCreateNewIfExpired() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); try (final StrictConnPool pool = new StrictConnPool<>(2, 2)) { final Future> future1 = pool.lease("somehost", null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); entry1.updateExpiry(TimeValue.of(1, TimeUnit.MILLISECONDS)); pool.release(entry1, true); Thread.sleep(200L); final Future> future2 = pool.lease("somehost", null); Assertions.assertTrue(future2.isDone()); Mockito.verify(conn1).close(CloseMode.GRACEFUL); final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(0, totals.getAvailable()); Assertions.assertEquals(1, totals.getLeased()); Assertions.assertEquals(Collections.singleton("somehost"), pool.getRoutes()); final PoolStats stats = pool.getStats("somehost"); Assertions.assertEquals(0, stats.getAvailable()); Assertions.assertEquals(1, stats.getLeased()); } } @Test public void testCloseExpired() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); final HttpConnection conn2 = Mockito.mock(HttpConnection.class); try (final StrictConnPool pool = new StrictConnPool<>(2, 2)) { final Future> future1 = pool.lease("somehost", null); final Future> future2 = pool.lease("somehost", null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); Assertions.assertTrue(future2.isDone()); final PoolEntry entry2 = future2.get(); Assertions.assertNotNull(entry2); entry2.assignConnection(conn2); entry1.updateExpiry(TimeValue.of(1, TimeUnit.MILLISECONDS)); pool.release(entry1, true); Thread.sleep(200); entry2.updateExpiry(TimeValue.of(1000, TimeUnit.SECONDS)); pool.release(entry2, true); pool.closeExpired(); Mockito.verify(conn1).close(CloseMode.GRACEFUL); Mockito.verify(conn2, Mockito.never()).close(ArgumentMatchers.any()); final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(1, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); final PoolStats stats = pool.getStats("somehost"); Assertions.assertEquals(1, stats.getAvailable()); Assertions.assertEquals(0, stats.getLeased()); Assertions.assertEquals(0, stats.getPending()); } } @Test public void testCloseIdle() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); final HttpConnection conn2 = Mockito.mock(HttpConnection.class); try (final StrictConnPool pool = new StrictConnPool<>(2, 2)) { final Future> future1 = pool.lease("somehost", null); final Future> future2 = pool.lease("somehost", null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); Assertions.assertTrue(future2.isDone()); final PoolEntry entry2 = future2.get(); Assertions.assertNotNull(entry2); entry2.assignConnection(conn2); entry1.updateState(null); pool.release(entry1, true); Thread.sleep(200L); entry2.updateState(null); pool.release(entry2, true); pool.closeIdle(TimeValue.of(50, TimeUnit.MILLISECONDS)); Mockito.verify(conn1).close(CloseMode.GRACEFUL); Mockito.verify(conn2, Mockito.never()).close(ArgumentMatchers.any()); PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(1, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); PoolStats stats = pool.getStats("somehost"); Assertions.assertEquals(1, stats.getAvailable()); Assertions.assertEquals(0, stats.getLeased()); Assertions.assertEquals(0, stats.getPending()); pool.closeIdle(TimeValue.of(-1, TimeUnit.MILLISECONDS)); Mockito.verify(conn2).close(CloseMode.GRACEFUL); totals = pool.getTotalStats(); Assertions.assertEquals(0, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); stats = pool.getStats("somehost"); Assertions.assertEquals(0, stats.getAvailable()); Assertions.assertEquals(0, stats.getLeased()); Assertions.assertEquals(0, stats.getPending()); } } @Test public void testLeaseRequestTimeout() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); try (final StrictConnPool pool = new StrictConnPool<>(1, 1)) { final Future> future1 = pool.lease("somehost", null, Timeout.ofMilliseconds(0), null); final Future> future2 = pool.lease("somehost", null, Timeout.ofMilliseconds(0), null); final Future> future3 = pool.lease("somehost", null, Timeout.ofMilliseconds(10), null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); Assertions.assertFalse(future2.isDone()); Assertions.assertFalse(future3.isDone()); Thread.sleep(100); pool.validatePendingRequests(); Assertions.assertFalse(future2.isDone()); Assertions.assertTrue(future3.isDone()); } } private static class HoldInternalLockThread extends Thread { private HoldInternalLockThread(final StrictConnPool pool, final CountDownLatch lockHeld) { super(() -> { pool.lease("somehost", null); // lease a connection so we have something to enumLeased() pool.enumLeased(object -> { try { lockHeld.countDown(); Thread.sleep(Long.MAX_VALUE); } catch (final InterruptedException ignored) { } }); }); } } @Test public void testLeaseRequestLockTimeout() throws Exception { final StrictConnPool pool = new StrictConnPool<>(1, 1); final CountDownLatch lockHeld = new CountDownLatch(1); final Thread holdInternalLock = new HoldInternalLockThread(pool, lockHeld); holdInternalLock.start(); // Start a thread to grab the internal conn pool lock lockHeld.await(); // Wait until we know the internal lock is held // Attempt to get a connection while lock is held final Future> future2 = pool.lease("somehost", null, Timeout.ofMilliseconds(10), null); final ExecutionException executionException = Assertions.assertThrows(ExecutionException.class, () -> future2.get()); Assertions.assertTrue(executionException.getCause() instanceof DeadlineTimeoutException); holdInternalLock.interrupt(); // Cleanup } @Test public void testLeaseRequestInterrupted() throws Exception { final StrictConnPool pool = new StrictConnPool<>(1, 1); final CountDownLatch lockHeld = new CountDownLatch(1); final Thread holdInternalLock = new HoldInternalLockThread(pool, lockHeld); holdInternalLock.start(); // Start a thread to grab the internal conn pool lock lockHeld.await(); // Wait until we know the internal lock is held Thread.currentThread().interrupt(); // Attempt to get a connection while lock is held and thread is interrupted final Future> future2 = pool.lease("somehost", null, Timeout.ofMilliseconds(10), null); Assertions.assertTrue(Thread.interrupted()); Assertions.assertThrows(CancellationException.class, () -> future2.get()); holdInternalLock.interrupt(); // Cleanup } @Test public void testLeaseRequestCanceled() throws Exception { try (final StrictConnPool pool = new StrictConnPool<>(1, 1)) { final Future> future1 = pool.lease("somehost", null, Timeout.ofMilliseconds(0), null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(Mockito.mock(HttpConnection.class)); final Future> future2 = pool.lease("somehost", null, Timeout.ofMilliseconds(0), null); future2.cancel(true); pool.release(entry1, true); final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(1, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); } } @Test public void testGetStatsInvalid() throws Exception { try (final StrictConnPool pool = new StrictConnPool<>(2, 2)) { Assertions.assertThrows(NullPointerException.class, () -> pool.getStats(null)); } } @Test public void testSetMaxInvalid() throws Exception { try (final StrictConnPool pool = new StrictConnPool<>(2, 2)) { Assertions.assertThrows(IllegalArgumentException.class, () -> pool.setMaxTotal(-1)); Assertions.assertThrows(NullPointerException.class, () -> pool.setMaxPerRoute(null, 1)); Assertions.assertThrows(IllegalArgumentException.class, () -> pool.setDefaultMaxPerRoute(-1)); } } @Test public void testSetMaxPerRoute() throws Exception { try (final StrictConnPool pool = new StrictConnPool<>(2, 2)) { pool.setMaxPerRoute("somehost", 1); Assertions.assertEquals(1, pool.getMaxPerRoute("somehost")); pool.setMaxPerRoute("somehost", 0); Assertions.assertEquals(0, pool.getMaxPerRoute("somehost")); pool.setMaxPerRoute("somehost", -1); Assertions.assertEquals(2, pool.getMaxPerRoute("somehost")); } } @Test public void testShutdown() throws Exception { final StrictConnPool pool = new StrictConnPool<>(2, 2); pool.close(CloseMode.GRACEFUL); Assertions.assertThrows(IllegalStateException.class, () -> pool.lease("somehost", null)); // Ignored if shut down pool.release(new PoolEntry<>("somehost"), true); } } httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolEntry.java0100664 0000000 0000000 00000013231 14403631147 024244 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Deadline; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestPoolEntry { private AtomicLong count; private Supplier currentTimeSupplier; @BeforeEach public void setup() { count = new AtomicLong(1); currentTimeSupplier = () -> count.addAndGet(1); } @Test public void testBasics() throws Exception { final PoolEntry entry1 = new PoolEntry<>( "route1", TimeValue.of(10L, TimeUnit.MILLISECONDS), currentTimeSupplier); Assertions.assertEquals("route1", entry1.getRoute()); Assertions.assertEquals(0, entry1.getUpdated()); Assertions.assertEquals(Deadline.MIN_VALUE, entry1.getExpiryDeadline()); entry1.assignConnection(Mockito.mock(HttpConnection.class)); final long now = System.currentTimeMillis(); Assertions.assertEquals("route1", entry1.getRoute()); Assertions.assertTrue(now >= entry1.getUpdated()); Assertions.assertEquals(entry1.getValidityDeadline(), entry1.getExpiryDeadline()); Assertions.assertEquals(entry1.getUpdated() + 10L, entry1.getValidityDeadline().getValue()); entry1.discardConnection(CloseMode.IMMEDIATE); Assertions.assertEquals(0, entry1.getUpdated()); Assertions.assertEquals(Deadline.MIN_VALUE, entry1.getExpiryDeadline()); } @Test public void testNullConstructor() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> new PoolEntry(null)); } @Test public void testValidInfinitely() throws Exception { final PoolEntry entry1 = new PoolEntry<>( "route1", TimeValue.ZERO_MILLISECONDS, currentTimeSupplier); entry1.assignConnection(Mockito.mock(HttpConnection.class)); Assertions.assertEquals(Deadline.MAX_VALUE, entry1.getValidityDeadline()); Assertions.assertEquals(entry1.getValidityDeadline(), entry1.getExpiryDeadline()); } @Test public void testExpiry() throws Exception { final PoolEntry entry1 = new PoolEntry<>( "route1", TimeValue.ZERO_MILLISECONDS, currentTimeSupplier); entry1.assignConnection(Mockito.mock(HttpConnection.class)); Assertions.assertEquals(Deadline.MAX_VALUE, entry1.getExpiryDeadline()); entry1.updateExpiry(TimeValue.of(50L, TimeUnit.MILLISECONDS)); Assertions.assertEquals(entry1.getUpdated() + 50L, entry1.getExpiryDeadline().getValue()); entry1.updateExpiry(TimeValue.ZERO_MILLISECONDS); Assertions.assertEquals(Deadline.MAX_VALUE, entry1.getExpiryDeadline()); final PoolEntry entry2 = new PoolEntry<>( "route1", TimeValue.of(100L, TimeUnit.MILLISECONDS), currentTimeSupplier); entry2.assignConnection(Mockito.mock(HttpConnection.class)); final Deadline validityDeadline = entry2.getValidityDeadline(); Assertions.assertEquals(entry2.getUpdated() + 100L, entry2.getExpiryDeadline().getValue()); entry2.updateExpiry(TimeValue.of(50L, TimeUnit.MILLISECONDS)); Assertions.assertEquals(entry2.getUpdated() + 50L, entry2.getExpiryDeadline().getValue()); entry2.updateExpiry(TimeValue.of(150L, TimeUnit.MILLISECONDS)); Assertions.assertEquals(validityDeadline, entry2.getExpiryDeadline()); } @Test public void testInvalidExpiry() throws Exception { final PoolEntry entry = new PoolEntry<>( "route1", TimeValue.of(0L, TimeUnit.MILLISECONDS), currentTimeSupplier); Assertions.assertThrows(NullPointerException.class, () -> entry.updateExpiry(null)); } @Test public void testExpiryDoesNotOverflow() { final PoolEntry entry = new PoolEntry<>( "route1", TimeValue.of(Long.MAX_VALUE, TimeUnit.MILLISECONDS), currentTimeSupplier); entry.assignConnection(Mockito.mock(HttpConnection.class)); Assertions.assertEquals(Deadline.MAX_VALUE, entry.getValidityDeadline()); } } httpcore5/src/test/java/org/apache/hc/core5/pool/TestLaxConnPool.java0100664 0000000 0000000 00000044610 14403631147 024512 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.pool; import java.util.Collections; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; public class TestLaxConnPool { @Test public void testEmptyPool() throws Exception { try (final LaxConnPool pool = new LaxConnPool<>(2)) { final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(0, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); Assertions.assertEquals(0, totals.getMax()); Assertions.assertEquals(Collections.emptySet(), pool.getRoutes()); final PoolStats stats = pool.getStats("somehost"); Assertions.assertEquals(0, stats.getAvailable()); Assertions.assertEquals(0, stats.getLeased()); Assertions.assertEquals(0, stats.getPending()); Assertions.assertEquals(2, stats.getMax()); Assertions.assertEquals("[leased: 0][available: 0][pending: 0]", pool.toString()); } } @Test public void testInvalidConstruction() throws Exception { Assertions.assertThrows(IllegalArgumentException.class, () -> new LaxConnPool(-1)); } @Test public void testLeaseRelease() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); final HttpConnection conn2 = Mockito.mock(HttpConnection.class); final HttpConnection conn3 = Mockito.mock(HttpConnection.class); try (final LaxConnPool pool = new LaxConnPool<>(2)) { final Future> future1 = pool.lease("somehost", null); final Future> future2 = pool.lease("somehost", null); final Future> future3 = pool.lease("otherhost", null); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); final PoolEntry entry2 = future2.get(); Assertions.assertNotNull(entry2); entry2.assignConnection(conn2); final PoolEntry entry3 = future3.get(); Assertions.assertNotNull(entry3); entry3.assignConnection(conn3); pool.release(entry1, true); pool.release(entry2, true); pool.release(entry3, false); Mockito.verify(conn1, Mockito.never()).close(ArgumentMatchers.any()); Mockito.verify(conn2, Mockito.never()).close(ArgumentMatchers.any()); Mockito.verify(conn3, Mockito.times(1)).close(CloseMode.GRACEFUL); final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(2, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); } } @Test public void testLeaseInvalid() throws Exception { try (final LaxConnPool pool = new LaxConnPool<>(2)) { Assertions.assertThrows(NullPointerException.class, () -> pool.lease(null, null, Timeout.ZERO_MILLISECONDS, null)); }} @Test public void testReleaseUnknownEntry() throws Exception { try (final LaxConnPool pool = new LaxConnPool<>(2)) { Assertions.assertThrows(IllegalStateException.class, () -> pool.release(new PoolEntry<>("somehost"), true)); } } @Test public void testMaxLimits() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); final HttpConnection conn2 = Mockito.mock(HttpConnection.class); final HttpConnection conn3 = Mockito.mock(HttpConnection.class); try (final LaxConnPool pool = new LaxConnPool<>(2)) { pool.setMaxPerRoute("somehost", 2); pool.setMaxPerRoute("otherhost", 1); final Future> future1 = pool.lease("somehost", null); final Future> future2 = pool.lease("somehost", null); final Future> future3 = pool.lease("otherhost", null); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); final PoolEntry entry2 = future2.get(); Assertions.assertNotNull(entry2); entry2.assignConnection(conn2); final PoolEntry entry3 = future3.get(); Assertions.assertNotNull(entry3); entry3.assignConnection(conn3); pool.release(entry1, true); pool.release(entry2, true); pool.release(entry3, true); final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(3, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); final Future> future4 = pool.lease("somehost", null); final Future> future5 = pool.lease("somehost", null); final Future> future6 = pool.lease("otherhost", null); final Future> future7 = pool.lease("somehost", null); final Future> future8 = pool.lease("somehost", null); final Future> future9 = pool.lease("otherhost", null); Assertions.assertTrue(future4.isDone()); final PoolEntry entry4 = future4.get(); Assertions.assertNotNull(entry4); Assertions.assertSame(conn2, entry4.getConnection()); Assertions.assertTrue(future5.isDone()); final PoolEntry entry5 = future5.get(); Assertions.assertNotNull(entry5); Assertions.assertSame(conn1, entry5.getConnection()); Assertions.assertTrue(future6.isDone()); final PoolEntry entry6 = future6.get(); Assertions.assertNotNull(entry6); Assertions.assertSame(conn3, entry6.getConnection()); Assertions.assertFalse(future7.isDone()); Assertions.assertFalse(future8.isDone()); Assertions.assertFalse(future9.isDone()); pool.release(entry4, true); pool.release(entry5, false); pool.release(entry6, true); Assertions.assertTrue(future7.isDone()); final PoolEntry entry7 = future7.get(); Assertions.assertNotNull(entry7); Assertions.assertSame(conn2, entry7.getConnection()); Assertions.assertTrue(future8.isDone()); final PoolEntry entry8 = future8.get(); Assertions.assertNotNull(entry8); Assertions.assertNull(entry8.getConnection()); Assertions.assertTrue(future9.isDone()); final PoolEntry entry9 = future9.get(); Assertions.assertNotNull(entry9); Assertions.assertSame(conn3, entry9.getConnection()); } } @Test public void testCreateNewIfExpired() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); try (final LaxConnPool pool = new LaxConnPool<>(2)) { final Future> future1 = pool.lease("somehost", null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); entry1.updateExpiry(TimeValue.of(1, TimeUnit.MILLISECONDS)); pool.release(entry1, true); Thread.sleep(200L); final Future> future2 = pool.lease("somehost", null); Assertions.assertTrue(future2.isDone()); Mockito.verify(conn1).close(CloseMode.GRACEFUL); final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(0, totals.getAvailable()); Assertions.assertEquals(1, totals.getLeased()); Assertions.assertEquals(Collections.singleton("somehost"), pool.getRoutes()); final PoolStats stats = pool.getStats("somehost"); Assertions.assertEquals(0, stats.getAvailable()); Assertions.assertEquals(1, stats.getLeased()); } } @Test public void testCloseExpired() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); final HttpConnection conn2 = Mockito.mock(HttpConnection.class); try (final LaxConnPool pool = new LaxConnPool<>(2)) { final Future> future1 = pool.lease("somehost", null); final Future> future2 = pool.lease("somehost", null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); Assertions.assertTrue(future2.isDone()); final PoolEntry entry2 = future2.get(); Assertions.assertNotNull(entry2); entry2.assignConnection(conn2); entry1.updateExpiry(TimeValue.of(1, TimeUnit.MILLISECONDS)); pool.release(entry1, true); Thread.sleep(200); entry2.updateExpiry(TimeValue.of(1000, TimeUnit.SECONDS)); pool.release(entry2, true); pool.closeExpired(); Mockito.verify(conn1).close(CloseMode.GRACEFUL); Mockito.verify(conn2, Mockito.never()).close(ArgumentMatchers.any()); final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(1, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); final PoolStats stats = pool.getStats("somehost"); Assertions.assertEquals(1, stats.getAvailable()); Assertions.assertEquals(0, stats.getLeased()); Assertions.assertEquals(0, stats.getPending()); } } @Test public void testCloseIdle() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); final HttpConnection conn2 = Mockito.mock(HttpConnection.class); try (final LaxConnPool pool = new LaxConnPool<>(2)) { final Future> future1 = pool.lease("somehost", null); final Future> future2 = pool.lease("somehost", null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); Assertions.assertTrue(future2.isDone()); final PoolEntry entry2 = future2.get(); Assertions.assertNotNull(entry2); entry2.assignConnection(conn2); entry1.updateState(null); pool.release(entry1, true); Thread.sleep(200L); entry2.updateState(null); pool.release(entry2, true); pool.closeIdle(TimeValue.of(50, TimeUnit.MILLISECONDS)); Mockito.verify(conn1).close(CloseMode.GRACEFUL); Mockito.verify(conn2, Mockito.never()).close(ArgumentMatchers.any()); PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(1, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); PoolStats stats = pool.getStats("somehost"); Assertions.assertEquals(1, stats.getAvailable()); Assertions.assertEquals(0, stats.getLeased()); Assertions.assertEquals(0, stats.getPending()); pool.closeIdle(TimeValue.of(-1, TimeUnit.MILLISECONDS)); Mockito.verify(conn2).close(CloseMode.GRACEFUL); totals = pool.getTotalStats(); Assertions.assertEquals(0, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); Assertions.assertEquals(0, totals.getPending()); stats = pool.getStats("somehost"); Assertions.assertEquals(0, stats.getAvailable()); Assertions.assertEquals(0, stats.getLeased()); Assertions.assertEquals(0, stats.getPending()); Assertions.assertFalse(pool.isShutdown()); } } @Test public void testLeaseRequestTimeout() throws Exception { final HttpConnection conn1 = Mockito.mock(HttpConnection.class); try (final LaxConnPool pool = new LaxConnPool<>(1)) { final Future> future1 = pool.lease("somehost", null, Timeout.ofMilliseconds(0), null); final Future> future2 = pool.lease("somehost", null, Timeout.ofMilliseconds(0), null); final Future> future3 = pool.lease("somehost", null, Timeout.ofMilliseconds(10), null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(conn1); Assertions.assertFalse(future2.isDone()); Assertions.assertFalse(future3.isDone()); Thread.sleep(100); pool.validatePendingRequests(); Assertions.assertFalse(future2.isDone()); Assertions.assertTrue(future3.isDone()); } } @Test public void testLeaseRequestCanceled() throws Exception { try (final LaxConnPool pool = new LaxConnPool<>(1)) { final Future> future1 = pool.lease("somehost", null, Timeout.ofMilliseconds(0), null); Assertions.assertTrue(future1.isDone()); final PoolEntry entry1 = future1.get(); Assertions.assertNotNull(entry1); entry1.assignConnection(Mockito.mock(HttpConnection.class)); final Future> future2 = pool.lease("somehost", null, Timeout.ofMilliseconds(0), null); future2.cancel(true); pool.release(entry1, true); final PoolStats totals = pool.getTotalStats(); Assertions.assertEquals(1, totals.getAvailable()); Assertions.assertEquals(0, totals.getLeased()); } } @Test public void testGetStatsInvalid() throws Exception { try (final LaxConnPool pool = new LaxConnPool<>(2)) { Assertions.assertThrows(NullPointerException.class, () -> pool.getStats(null)); } } @Test public void testSetMaxInvalid() throws Exception { try (final LaxConnPool pool = new LaxConnPool<>(2)) { Assertions.assertThrows(NullPointerException.class, () -> pool.setMaxPerRoute(null, 1)); Assertions.assertThrows(IllegalArgumentException.class, () -> pool.setDefaultMaxPerRoute(-1)); } } @Test public void testShutdown() throws Exception { final LaxConnPool pool = new LaxConnPool<>(2); pool.close(CloseMode.GRACEFUL); Assertions.assertThrows(IllegalStateException.class, () -> pool.lease("somehost", null)); // Ignored if shut down pool.release(new PoolEntry<>("somehost"), true); } @Test public void testClose() { final LaxConnPool pool = new LaxConnPool<>(2); pool.setMaxPerRoute("someRoute", 2); pool.close(); Assertions.assertThrows(IllegalStateException.class, () -> pool.lease("someHost", null)); // Ignored if shut down pool.release(new PoolEntry<>("someHost"), true); } @Test public void testGetMaxPerRoute() { final String route = "someRoute"; final int max = 2; try (final LaxConnPool pool = new LaxConnPool<>(2)) { pool.setMaxPerRoute(route, max); Assertions.assertEquals(max, pool.getMaxPerRoute(route)); } } } httpcore5/src/test/java/org/apache/hc/core5/annotation/0040775 0000000 0000000 00000000000 14403631147 022012 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/annotation/ThreadingBehaviorTest.java0100664 0000000 0000000 00000003471 14403631147 027104 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.annotation; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; public class ThreadingBehaviorTest { @Test void testName(){ assertEquals("SAFE", ThreadingBehavior.SAFE.name()); assertEquals("SAFE_CONDITIONAL", ThreadingBehavior.SAFE_CONDITIONAL.name()); assertEquals("IMMUTABLE_CONDITIONAL", ThreadingBehavior.IMMUTABLE_CONDITIONAL.name()); assertEquals("IMMUTABLE", ThreadingBehavior.IMMUTABLE.name()); assertEquals("STATELESS", ThreadingBehavior.STATELESS.name()); assertEquals("UNSAFE", ThreadingBehavior.UNSAFE.name()); } }httpcore5/src/test/java/org/apache/hc/core5/ssl/0040775 0000000 0000000 00000000000 14403631147 020441 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/ssl/DummyProvider.java0100664 0000000 0000000 00000004431 14403631147 024111 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.ssl; import java.security.Provider; import java.security.Security; import java.util.HashSet; import java.util.Set; public class DummyProvider extends Provider { private final Provider realJSSEProvider = Security.getProvider(TestSSLContextBuilder.PROVIDER_SUN_JSSE); private final Provider realJCEEProvider = Security.getProvider(TestSSLContextBuilder.PROVIDER_SUN_JCE); final static String NAME = "FAKE"; private final Set requestedTypes = new HashSet<>(); public DummyProvider() { super(NAME, 1.1, "http core fake provider 1.1"); } public boolean hasBeenRequested(final String what) { return requestedTypes.contains(what); } @Override public Service getService(final String type, final String algorithm) { requestedTypes.add(type); if ("KeyStore".equals(type)) { return realJCEEProvider.getService(type, algorithm); } return realJSSEProvider.getService(type, algorithm); } @Override public synchronized Set getServices() { return realJSSEProvider.getServices(); } } httpcore5/src/test/java/org/apache/hc/core5/ssl/SSLContextsTest.java0100664 0000000 0000000 00000006532 14403631147 024340 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.ssl; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import org.junit.jupiter.api.Test; public class SSLContextsTest { @Test void createDefault() { final SSLContext sslContext = SSLContexts.createDefault(); assertAll( () -> assertNotNull(sslContext), () -> assertEquals(SSLContextBuilder.TLS, sslContext.getProtocol()), () -> assertNotNull(sslContext.getProvider()) ); } @Test void createSystemDefault() { final SSLContext sslContext = SSLContexts.createSystemDefault(); assertAll( () -> assertNotNull(sslContext), () -> assertEquals("Default", sslContext.getProtocol()), () -> assertNotNull(sslContext.getProvider()) ); } @Test void custom() throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, KeyManagementException { final SSLContext sslContext = SSLContexts.custom() .setKeyStoreType(KeyStore.getDefaultType()) .setKeyManagerFactoryAlgorithm(KeyManagerFactory.getDefaultAlgorithm()) .setTrustManagerFactoryAlgorithm(TrustManagerFactory.getDefaultAlgorithm()) .setProvider("SunJSSE") .setProtocol("TLS") .setSecureRandom(null) .loadTrustMaterial((KeyStore) null, null) .loadKeyMaterial((KeyStore) null, null, null) .build(); assertAll( () -> assertNotNull(sslContext), () -> assertEquals(SSLContextBuilder.TLS, sslContext.getProtocol()), () -> assertEquals("SunJSSE", sslContext.getProvider().getName()) ); } }httpcore5/src/test/java/org/apache/hc/core5/ssl/TestSSLContextBuilder.java0100664 0000000 0000000 00000101733 14403631147 025463 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.ssl; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.URL; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Principal; import java.security.Security; import java.security.UnrecoverableKeyException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManagerFactory; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link SSLContextBuilder}. */ public class TestSSLContextBuilder { static final String PROVIDER_SUN_JSSE = "SunJSSE"; static final String PROVIDER_SUN_JCE = "SunJCE"; private static boolean isWindows() { return System.getProperty("os.name").contains("Windows"); } private static final Timeout TIMEOUT = Timeout.ofSeconds(5); private ExecutorService executorService; @AfterEach public void cleanup() throws Exception { if (this.executorService != null) { this.executorService.shutdown(); this.executorService.awaitTermination(5, TimeUnit.SECONDS); } } private URL getResource(final String name) { return getClass().getResource(name); } @Test public void testBuildAllDefaults() throws Exception { final SSLContext sslContext = SSLContextBuilder.create() .setKeyStoreType(KeyStore.getDefaultType()) .setKeyManagerFactoryAlgorithm(KeyManagerFactory.getDefaultAlgorithm()) .setTrustManagerFactoryAlgorithm(TrustManagerFactory.getDefaultAlgorithm()) .setProvider(PROVIDER_SUN_JSSE) .setProtocol("TLS") .setSecureRandom(null) .loadTrustMaterial((KeyStore) null, null) .loadKeyMaterial((KeyStore) null, null, null) .build(); Assertions.assertNotNull(sslContext); Assertions.assertEquals("TLS", sslContext.getProtocol()); Assertions.assertEquals(PROVIDER_SUN_JSSE, sslContext.getProvider().getName()); } @Test public void testBuildAllNull() throws Exception { final SSLContext sslContext = SSLContextBuilder.create() .setKeyStoreType(null) .setKeyManagerFactoryAlgorithm(null) .setTrustManagerFactoryAlgorithm(null) .setProtocol(null) .setProvider((String) null) .setSecureRandom(null) .loadTrustMaterial((KeyStore) null, null) .loadKeyMaterial((KeyStore) null, null, null) .build(); Assertions.assertNotNull(sslContext); Assertions.assertEquals("TLS", sslContext.getProtocol()); Assertions.assertEquals(PROVIDER_SUN_JSSE, sslContext.getProvider().getName()); } @Test public void testBuildAllNull_deprecated() throws Exception { final SSLContext sslContext = SSLContextBuilder.create() .setProtocol(null) .setSecureRandom(null) .loadTrustMaterial((KeyStore) null, null) .loadKeyMaterial((KeyStore) null, null, null) .build(); Assertions.assertNotNull(sslContext); Assertions.assertEquals("TLS", sslContext.getProtocol()); } @Test public void testBuildDefault() throws Exception { new SSLContextBuilder().build(); } @Test public void testBuildNoSuchKeyManagerFactoryAlgorithm() throws Exception { final URL resource1 = getResource("/test-keypasswd.p12"); final String storePassword = "nopassword"; final String keyPassword = "password"; Assertions.assertThrows(NoSuchAlgorithmException.class, () -> SSLContextBuilder.create() .setKeyManagerFactoryAlgorithm(" BAD ") .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build()); } @Test public void testBuildNoSuchKeyStoreType() throws Exception { final URL resource1 = getResource("/test-keypasswd.p12"); final String storePassword = "nopassword"; final String keyPassword = "password"; Assertions.assertThrows(KeyStoreException.class, () -> SSLContextBuilder.create() .setKeyStoreType(" BAD ") .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build()); } @Test public void testBuildNoSuchTrustManagerFactoryAlgorithm() throws Exception { final URL resource1 = getResource("/test-keypasswd.p12"); final String storePassword = "nopassword"; Assertions.assertThrows(NoSuchAlgorithmException.class, () -> SSLContextBuilder.create() .setTrustManagerFactoryAlgorithm(" BAD ") .loadTrustMaterial(resource1, storePassword.toCharArray()) .build()); } @Test public void testBuildWithProvider() throws Exception { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; final DummyProvider provider = new DummyProvider(); SSLContextBuilder.create() .setProvider(provider) .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertTrue(provider.hasBeenRequested("SSLContext")); } @Test public void testBuildWithProviderName() throws Exception { final DummyProvider provider = new DummyProvider(); Security.insertProviderAt(provider, 1); try { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; SSLContextBuilder.create() .setProvider(DummyProvider.NAME) .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertTrue(provider.hasBeenRequested("SSLContext")); } finally { Security.removeProvider(DummyProvider.NAME); } } @Test public void testBuildKSWithNoSuchProvider() { Assertions.assertThrows(NoSuchProviderException.class, () -> SSLContextBuilder.create() .setKeyStoreProvider("no-such-provider") .build()); } @Test public void testBuildKSWithProvider() throws Exception { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; final DummyProvider provider = new DummyProvider(); SSLContextBuilder.create() .setKeyStoreProvider(provider) .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertTrue(provider.hasBeenRequested("KeyManagerFactory")); } @Test public void testBuildKSWithProviderName() throws Exception { final DummyProvider provider = new DummyProvider(); Security.insertProviderAt(provider, 1); try { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; SSLContextBuilder.create() .setKeyStoreProvider(DummyProvider.NAME) .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertTrue(provider.hasBeenRequested("KeyManagerFactory")); } finally { Security.removeProvider(DummyProvider.NAME); } } @Test public void testBuildTSWithNoSuchProvider() { Assertions.assertThrows(NoSuchProviderException.class, ()-> SSLContextBuilder.create() .setTrustStoreProvider("no-such-provider") .build()); } @Test public void testBuildTSWithProvider() throws Exception { final DummyProvider provider = new DummyProvider(); SSLContextBuilder.create() .setTrustStoreProvider(provider) .loadTrustMaterial((KeyStore) null, null) .build(); Assertions.assertTrue(provider.hasBeenRequested("TrustManagerFactory")); } @Test public void testBuildTSWithProviderName() throws Exception { final DummyProvider provider = new DummyProvider(); Security.insertProviderAt(provider, 1); try { SSLContextBuilder.create() .setTrustStoreProvider(DummyProvider.NAME) .loadTrustMaterial((KeyStore) null, null) .build(); Assertions.assertTrue(provider.hasBeenRequested("TrustManagerFactory")); } finally { Security.removeProvider(DummyProvider.NAME); } } @Test public void testKeyWithAlternatePasswordInvalid() throws Exception { final URL resource1 = getResource("/test-keypasswd.p12"); final String storePassword = "nopassword"; final String keyPassword = "!password"; Assertions.assertThrows(UnrecoverableKeyException.class, () -> SSLContextBuilder.create() .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .loadTrustMaterial(resource1, storePassword.toCharArray()) .build()); } @Test public void testSSLHandshakeServerTrusted() throws Exception { final URL resource1 = getResource("/test.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; final SSLContext serverSslContext = SSLContextBuilder.create() .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertNotNull(serverSslContext); final SSLContext clientSslContext = SSLContextBuilder.create() .loadTrustMaterial(resource1, storePassword.toCharArray()) .build(); Assertions.assertNotNull(clientSslContext); final ServerSocket serverSocket = serverSslContext.getServerSocketFactory().createServerSocket(); serverSocket.bind(new InetSocketAddress(0)); this.executorService = Executors.newSingleThreadExecutor(); final Future future = this.executorService.submit(() -> { try (Socket socket = serverSocket.accept()) { final OutputStream outputStream = socket.getOutputStream(); outputStream.write(new byte[]{'H', 'i'}); outputStream.flush(); } return Boolean.TRUE; }); final int localPort = serverSocket.getLocalPort(); try (final Socket clientSocket = clientSslContext.getSocketFactory().createSocket()) { clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound()); clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound()); final InputStream inputStream = clientSocket.getInputStream(); Assertions.assertEquals('H', inputStream.read()); Assertions.assertEquals('i', inputStream.read()); Assertions.assertEquals(-1, inputStream.read()); } final Boolean result = future.get(5, TimeUnit.SECONDS); Assertions.assertNotNull(result); } @Test public void testSSLHandshakeServerNotTrusted() throws Exception { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; final SSLContext serverSslContext = SSLContextBuilder.create() .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertNotNull(serverSslContext); final URL resource2 = getResource("/test.p12"); final SSLContext clientSslContext = SSLContextBuilder.create() .loadTrustMaterial(resource2, storePassword.toCharArray()) .build(); Assertions.assertNotNull(clientSslContext); final ServerSocket serverSocket = serverSslContext.getServerSocketFactory().createServerSocket(); serverSocket.bind(new InetSocketAddress(0)); this.executorService = Executors.newSingleThreadExecutor(); this.executorService.submit(() -> { try (SSLSocket socket = (SSLSocket) serverSocket.accept()) { socket.getSession(); } return Boolean.FALSE; }); final int localPort = serverSocket.getLocalPort(); try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) { clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound()); clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound()); Assertions.assertThrows(IOException.class, clientSocket::startHandshake); } } @Test public void testSSLHandshakeServerCustomTrustStrategy() throws Exception { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; final SSLContext serverSslContext = SSLContextBuilder.create() .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertNotNull(serverSslContext); final AtomicReference certChainRef = new AtomicReference<>(); final TrustStrategy trustStrategy = (chain, authType) -> { certChainRef.set(chain); return true; }; final SSLContext clientSslContext = SSLContextBuilder.create() .loadTrustMaterial(trustStrategy) .build(); Assertions.assertNotNull(clientSslContext); final ServerSocket serverSocket = serverSslContext.getServerSocketFactory().createServerSocket(); serverSocket.bind(new InetSocketAddress(0)); this.executorService = Executors.newSingleThreadExecutor(); final Future future = this.executorService.submit(() -> { try (Socket socket = serverSocket.accept()) { final OutputStream outputStream = socket.getOutputStream(); outputStream.write(new byte[]{'H', 'i'}); outputStream.flush(); } return Boolean.TRUE; }); final int localPort = serverSocket.getLocalPort(); try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) { clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound()); clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound()); final InputStream inputStream = clientSocket.getInputStream(); Assertions.assertEquals('H', inputStream.read()); Assertions.assertEquals('i', inputStream.read()); Assertions.assertEquals(-1, inputStream.read()); } final Boolean result = future.get(5, TimeUnit.SECONDS); Assertions.assertNotNull(result); final X509Certificate[] certs = certChainRef.get(); Assertions.assertNotNull(certs); Assertions.assertEquals(2, certs.length); final X509Certificate cert1 = certs[0]; final Principal subjectDN1 = cert1.getSubjectDN(); Assertions.assertNotNull(subjectDN1); Assertions.assertEquals("CN=Test Server, OU=HttpComponents Project, O=Apache Software Foundation", subjectDN1.getName()); final X509Certificate cert2 = certs[1]; final Principal subjectDN2 = cert2.getSubjectDN(); Assertions.assertNotNull(subjectDN2); Assertions.assertEquals("EMAILADDRESS=dev@hc.apache.org, " + "CN=Test CA, OU=HttpComponents Project, O=Apache Software Foundation", subjectDN2.getName()); final Principal issuerDN = cert2.getIssuerDN(); Assertions.assertNotNull(issuerDN); Assertions.assertEquals("EMAILADDRESS=dev@hc.apache.org, " + "CN=Test CA, OU=HttpComponents Project, O=Apache Software Foundation", issuerDN.getName()); } @Test public void testSSLHandshakeClientUnauthenticated() throws Exception { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; final SSLContext serverSslContext = SSLContextBuilder.create() .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertNotNull(serverSslContext); final URL resource2 = getResource("/test-client.p12"); final SSLContext clientSslContext = SSLContextBuilder.create() .loadTrustMaterial(resource2, storePassword.toCharArray()) .build(); Assertions.assertNotNull(clientSslContext); final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket(); serverSocket.setWantClientAuth(true); serverSocket.bind(new InetSocketAddress(0)); this.executorService = Executors.newSingleThreadExecutor(); final Future future = this.executorService.submit(() -> { final SSLSocket socket = (SSLSocket) serverSocket.accept(); Principal clientPrincipal = null; try { final SSLSession session = socket.getSession(); try { clientPrincipal = session.getPeerPrincipal(); } catch (final SSLPeerUnverifiedException ignore) { } final OutputStream outputStream = socket.getOutputStream(); outputStream.write(new byte [] {'H', 'i'}); outputStream.flush(); } finally { socket.close(); } return clientPrincipal; }); final int localPort = serverSocket.getLocalPort(); try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) { clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound()); clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound()); clientSocket.startHandshake(); final InputStream inputStream = clientSocket.getInputStream(); Assertions.assertEquals('H', inputStream.read()); Assertions.assertEquals('i', inputStream.read()); Assertions.assertEquals(-1, inputStream.read()); } final Principal clientPrincipal = future.get(5, TimeUnit.SECONDS); Assertions.assertNull(clientPrincipal); } @Test public void testSSLHandshakeClientUnauthenticatedError() throws Exception { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; final SSLContext serverSslContext = SSLContextBuilder.create() .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertNotNull(serverSslContext); final URL resource2 = getResource("/test-client.p12"); final SSLContext clientSslContext = SSLContextBuilder.create() .loadTrustMaterial(resource2, storePassword.toCharArray()) .build(); Assertions.assertNotNull(clientSslContext); final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket(); serverSocket.setNeedClientAuth(true); serverSocket.bind(new InetSocketAddress(0)); this.executorService = Executors.newSingleThreadExecutor(); this.executorService.submit(() -> { try (SSLSocket socket = (SSLSocket) serverSocket.accept()) { socket.getSession(); } return Boolean.FALSE; }); final int localPort = serverSocket.getLocalPort(); try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) { clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound()); clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound()); Assertions.assertThrows(IOException.class, () -> { clientSocket.startHandshake(); final InputStream inputStream = clientSocket.getInputStream(); inputStream.read(); }); } } @Test public void testSSLHandshakeClientAuthenticated() throws Exception { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; final SSLContext serverSslContext = SSLContextBuilder.create() .loadTrustMaterial(resource1, storePassword.toCharArray()) .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertNotNull(serverSslContext); final URL resource2 = getResource("/test-client.p12"); final SSLContext clientSslContext = SSLContextBuilder.create() .loadTrustMaterial(resource2, storePassword.toCharArray()) .loadKeyMaterial(resource2, storePassword.toCharArray(), storePassword.toCharArray()) .build(); Assertions.assertNotNull(clientSslContext); final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket(); serverSocket.setNeedClientAuth(true); serverSocket.bind(new InetSocketAddress(0)); this.executorService = Executors.newSingleThreadExecutor(); final Future future = this.executorService.submit(() -> { try (SSLSocket socket = (SSLSocket) serverSocket.accept()) { final SSLSession session = socket.getSession(); final Principal clientPrincipal = session.getPeerPrincipal(); final OutputStream outputStream = socket.getOutputStream(); outputStream.write(new byte[]{'H', 'i'}); outputStream.flush(); return clientPrincipal; } }); final int localPort = serverSocket.getLocalPort(); try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) { clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound()); clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound()); clientSocket.startHandshake(); final InputStream inputStream = clientSocket.getInputStream(); Assertions.assertEquals('H', inputStream.read()); Assertions.assertEquals('i', inputStream.read()); Assertions.assertEquals(-1, inputStream.read()); } final Principal clientPrincipal = future.get(5, TimeUnit.SECONDS); Assertions.assertNotNull(clientPrincipal); } @Test public void testSSLHandshakeClientAuthenticatedPrivateKeyStrategy() throws Exception { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; final SSLContext serverSslContext = SSLContextBuilder.create() .loadTrustMaterial(resource1, storePassword.toCharArray()) .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertNotNull(serverSslContext); final PrivateKeyStrategy privateKeyStrategy = (aliases, sslParameters) -> aliases.containsKey("client2") ? "client2" : null; final URL resource2 = getResource("/test-client.p12"); final SSLContext clientSslContext = SSLContextBuilder.create() .loadTrustMaterial(resource2, storePassword.toCharArray()) .loadKeyMaterial(resource2, storePassword.toCharArray(), storePassword.toCharArray(), privateKeyStrategy) .build(); Assertions.assertNotNull(clientSslContext); final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket(); serverSocket.setNeedClientAuth(true); serverSocket.bind(new InetSocketAddress(0)); this.executorService = Executors.newSingleThreadExecutor(); final Future future = this.executorService.submit(() -> { try (SSLSocket socket = (SSLSocket) serverSocket.accept()) { final SSLSession session = socket.getSession(); final Principal clientPrincipal = session.getPeerPrincipal(); final OutputStream outputStream = socket.getOutputStream(); outputStream.write(new byte[]{'H', 'i'}); outputStream.flush(); return clientPrincipal; } }); final int localPort = serverSocket.getLocalPort(); try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) { clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound()); clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound()); clientSocket.startHandshake(); final InputStream inputStream = clientSocket.getInputStream(); Assertions.assertEquals('H', inputStream.read()); Assertions.assertEquals('i', inputStream.read()); Assertions.assertEquals(-1, inputStream.read()); } final Principal clientPrincipal = future.get(5, TimeUnit.SECONDS); Assertions.assertNotNull(clientPrincipal); Assertions.assertEquals("CN=Test Client 2,OU=HttpComponents Project,O=Apache Software Foundation", clientPrincipal.getName()); } @Test public void testSSLHandshakeProtocolMismatch1() throws Exception { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; final SSLContext serverSslContext = SSLContextBuilder.create() .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertNotNull(serverSslContext); final URL resource2 = getResource("/test-client.p12"); final SSLContext clientSslContext = SSLContextBuilder.create() .loadTrustMaterial(resource2, storePassword.toCharArray()) .build(); Assertions.assertNotNull(clientSslContext); final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket(); final Set supportedServerProtocols = new LinkedHashSet<>(Arrays.asList(serverSocket.getSupportedProtocols())); Assertions.assertTrue(supportedServerProtocols.contains("TLSv1")); serverSocket.setEnabledProtocols(new String[] {"TLSv1"}); serverSocket.bind(new InetSocketAddress(0)); this.executorService = Executors.newSingleThreadExecutor(); this.executorService.submit(() -> { try (SSLSocket socket = (SSLSocket) serverSocket.accept()) { socket.getSession(); } return Boolean.FALSE; }); final int localPort = serverSocket.getLocalPort(); try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) { final Set supportedClientProtocols = new LinkedHashSet<>(Arrays.asList(clientSocket.getSupportedProtocols())); Assertions.assertTrue(supportedClientProtocols.contains("SSLv3")); clientSocket.setEnabledProtocols(new String[] {"SSLv3"} ); clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound()); clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound()); if (isWindows()) { Assertions.assertThrows(IOException.class, clientSocket::startHandshake); } else { Assertions.assertThrows(SSLException.class, clientSocket::startHandshake); } } } @Test public void testSSLHandshakeProtocolMismatch2() throws Exception { final URL resource1 = getResource("/test-server.p12"); final String storePassword = "nopassword"; final String keyPassword = "nopassword"; final SSLContext serverSslContext = SSLContextBuilder.create() .loadKeyMaterial(resource1, storePassword.toCharArray(), keyPassword.toCharArray()) .build(); Assertions.assertNotNull(serverSslContext); final URL resource2 = getResource("/test-client.p12"); final SSLContext clientSslContext = SSLContextBuilder.create() .loadTrustMaterial(resource2, storePassword.toCharArray()) .build(); Assertions.assertNotNull(clientSslContext); final SSLServerSocket serverSocket = (SSLServerSocket) serverSslContext.getServerSocketFactory().createServerSocket(); final Set supportedServerProtocols = new LinkedHashSet<>(Arrays.asList(serverSocket.getSupportedProtocols())); Assertions.assertTrue(supportedServerProtocols.contains("SSLv3")); serverSocket.setEnabledProtocols(new String[] {"SSLv3"}); serverSocket.bind(new InetSocketAddress(0)); this.executorService = Executors.newSingleThreadExecutor(); this.executorService.submit(() -> { try (SSLSocket socket = (SSLSocket) serverSocket.accept()) { socket.getSession(); } return Boolean.FALSE; }); final int localPort = serverSocket.getLocalPort(); try (final SSLSocket clientSocket = (SSLSocket) clientSslContext.getSocketFactory().createSocket()) { final Set supportedClientProtocols = new LinkedHashSet<>( Arrays.asList(clientSocket.getSupportedProtocols())); Assertions.assertTrue(supportedClientProtocols.contains("TLSv1")); clientSocket.setEnabledProtocols(new String[]{"TLSv1"}); clientSocket.connect(new InetSocketAddress("localhost", localPort), TIMEOUT.toMillisecondsIntBound()); clientSocket.setSoTimeout(TIMEOUT.toMillisecondsIntBound()); if (isWindows()) { Assertions.assertThrows(IOException.class, clientSocket::startHandshake); } else { Assertions.assertThrows(SSLException.class, clientSocket::startHandshake); } } } } httpcore5/src/test/java/org/apache/hc/core5/http/0040775 0000000 0000000 00000000000 14435411723 020620 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/config/0040775 0000000 0000000 00000000000 14403631147 022064 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/config/TestRegistry.java0100664 0000000 0000000 00000003306 14403631147 025376 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.config; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestRegistry { @Test public void testCompleted() throws Exception { final Registry reg = RegistryBuilder.create().register("Stuff", "Stuff").build(); Assertions.assertEquals("Stuff", reg.lookup("Stuff")); Assertions.assertEquals("Stuff", reg.lookup("stuff")); Assertions.assertNull(reg.lookup("miss")); Assertions.assertNull(reg.lookup(null)); } } httpcore5/src/test/java/org/apache/hc/core5/http/config/TestNamedElementChain.java0100664 0000000 0000000 00000012275 14403631147 027074 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.config; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.CoreMatchers.is; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; /** * Tests for {@link NamedElementChain}. */ public class TestNamedElementChain { @Test public void testBasics() { final NamedElementChain list = new NamedElementChain<>(); assertThat(list.getFirst(), CoreMatchers.nullValue()); assertThat(list.getLast(), CoreMatchers.nullValue()); final NamedElementChain.Node nodeA = list.addFirst('a', "a"); assertThat(list.getFirst(), CoreMatchers.sameInstance(nodeA)); assertThat(list.getLast(), CoreMatchers.sameInstance(nodeA)); final NamedElementChain.Node nodeB = list.addLast('b', "b"); assertThat(list.getFirst(), CoreMatchers.sameInstance(nodeA)); assertThat(list.getLast(), CoreMatchers.sameInstance(nodeB)); final NamedElementChain.Node nodeZ = list.addLast('z', "z"); assertThat(list.getFirst(), CoreMatchers.sameInstance(nodeA)); assertThat(list.getLast(), CoreMatchers.sameInstance(nodeZ)); assertThat(nodeA.getPrevious(), CoreMatchers.nullValue()); assertThat(nodeA.getNext(), CoreMatchers.sameInstance(nodeB)); assertThat(nodeB.getPrevious(), CoreMatchers.sameInstance(nodeA)); assertThat(nodeB.getNext(), CoreMatchers.sameInstance(nodeZ)); assertThat(nodeZ.getPrevious(), CoreMatchers.sameInstance(nodeB)); assertThat(nodeZ.getNext(), CoreMatchers.nullValue()); final NamedElementChain.Node nodeD = list.addAfter("b", 'd', "d"); assertThat(nodeD.getPrevious(), CoreMatchers.sameInstance(nodeB)); assertThat(nodeD.getNext(), CoreMatchers.sameInstance(nodeZ)); assertThat(nodeB.getNext(), CoreMatchers.sameInstance(nodeD)); assertThat(nodeZ.getPrevious(), CoreMatchers.sameInstance(nodeD)); final NamedElementChain.Node nodeC = list.addBefore("d", 'c', "c"); assertThat(nodeC.getPrevious(), CoreMatchers.sameInstance(nodeB)); assertThat(nodeC.getNext(), CoreMatchers.sameInstance(nodeD)); assertThat(nodeB.getNext(), CoreMatchers.sameInstance(nodeC)); assertThat(nodeD.getPrevious(), CoreMatchers.sameInstance(nodeC)); assertThat(list.getSize(), CoreMatchers.equalTo(5)); assertThat(list.remove("a"), CoreMatchers.is(true)); assertThat(list.remove("z"), CoreMatchers.is(true)); assertThat(list.remove("c"), CoreMatchers.is(true)); assertThat(list.remove("c"), CoreMatchers.is(false)); assertThat(list.remove("blah"), CoreMatchers.is(false)); assertThat(list.getFirst(), CoreMatchers.sameInstance(nodeB)); assertThat(list.getLast(), CoreMatchers.sameInstance(nodeD)); assertThat(list.getSize(), CoreMatchers.equalTo(2)); assertThat(list.addBefore("blah", 'e', "e"), CoreMatchers.nullValue()); assertThat(list.getSize(), CoreMatchers.equalTo(2)); assertThat(list.addAfter("yada", 'e', "e"), CoreMatchers.nullValue()); assertThat(list.getSize(), CoreMatchers.equalTo(2)); } @Test public void testFind() { final NamedElementChain list = new NamedElementChain<>(); list.addLast('c', "c"); assertThat(list.find("c"), notNullValue()); assertThat(list.find("a"), nullValue()); } @Test public void testReplace() { final NamedElementChain list = new NamedElementChain<>(); list.addLast('c', "c"); final boolean found = list.replace("c",'z' ); assertThat(found, is(true)); assertThat(list.find("c").getValue(), equalTo('z')); assertThat(list.find("c").getName(), equalTo("c")); final boolean notFound = list.replace("X",'z' ); assertThat(notFound, is(false)); } } httpcore5/src/test/java/org/apache/hc/core5/http/NameValuePairListMatcher.java0100664 0000000 0000000 00000005646 14403631147 026323 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; public class NameValuePairListMatcher extends BaseMatcher> { private final List nvps; public NameValuePairListMatcher(final List nvps) { this.nvps = nvps; } @Override public boolean matches(final Object item) { if (item instanceof List) { final List objects = (List) item; if (objects.size() != nvps.size()) { return false; } for (int i = 1; i < objects.size(); i++) { final Object obj = objects.get(i); if (obj instanceof NameValuePair) { final NameValuePair nvp = (NameValuePair) obj; final NameValuePair expected = nvps.get(i); if (!Objects.equals(nvp.getName(), expected.getName()) || !Objects.equals(nvp.getValue(), expected.getValue())) { return false; } } } return true; } return false; } @Override public void describeTo(final Description description) { description.appendText("equals ").appendValueList("[", ";", "]", nvps); } public static Matcher> equalsTo(final NameValuePair... nvps) { return new NameValuePairListMatcher(Arrays.asList(nvps)); } public static Matcher> isEmpty() { return new NameValuePairListMatcher(Collections.emptyList()); } } httpcore5/src/test/java/org/apache/hc/core5/http/support/0040775 0000000 0000000 00000000000 14435411677 022344 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/support/TestBasicMessageBuilders.java0100664 0000000 0000000 00000027422 14435411677 030073 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.support; import static org.hamcrest.MatcherAssert.assertThat; import org.apache.hc.core5.http.HeaderMatcher; import org.apache.hc.core5.http.HeadersMatcher; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.net.URIAuthority; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Arrays; /** * Simple tests for {@link BasicResponseBuilder} and {@link BasicRequestBuilder}. */ public class TestBasicMessageBuilders { @Test public void testResponseBasics() throws Exception { final BasicResponseBuilder builder = BasicResponseBuilder.create(200); Assertions.assertEquals(200, builder.getStatus()); Assertions.assertNull(builder.getHeaders()); Assertions.assertNull(builder.getVersion()); final BasicHttpResponse r1 = builder.build(); Assertions.assertNotNull(r1); Assertions.assertEquals(200, r1.getCode()); Assertions.assertNull(r1.getVersion()); builder.setStatus(500); builder.setVersion(HttpVersion.HTTP_1_0); Assertions.assertEquals(500, builder.getStatus()); Assertions.assertEquals(HttpVersion.HTTP_1_0, builder.getVersion()); final BasicHttpResponse r2 = builder.build(); Assertions.assertEquals(500, r2.getCode()); Assertions.assertEquals(HttpVersion.HTTP_1_0, r2.getVersion()); builder.addHeader("h1", "v1"); builder.addHeader("h1", "v2"); builder.addHeader("h2", "v2"); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); assertThat(builder.getHeaders("h1"), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"))); assertThat(builder.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1")); assertThat(builder.getLastHeader("h1"), HeaderMatcher.same("h1", "v2")); final BasicHttpResponse r3 = builder.build(); assertThat(r3.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); assertThat(r3.getHeaders("h1"), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"))); assertThat(r3.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1")); assertThat(r3.getLastHeader("h1"), HeaderMatcher.same("h1", "v2")); builder.removeHeader(new BasicHeader("h1", "v2")); assertThat(builder.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1"))); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2"))); final BasicHttpResponse r4 = builder.build(); assertThat(r4.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1"))); assertThat(r4.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2"))); builder.removeHeaders("h1"); assertThat(builder.getHeaders("h1"), HeadersMatcher.same()); assertThat(builder.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2"))); final BasicHttpResponse r5 = builder.build(); assertThat(r5.getHeaders("h1"), HeadersMatcher.same()); assertThat(r5.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2"))); } @Test public void testRequestBasics() throws Exception { final BasicRequestBuilder builder = BasicRequestBuilder.get(); Assertions.assertEquals(URI.create("/"), builder.getUri()); Assertions.assertEquals("GET", builder.getMethod()); Assertions.assertNull(builder.getScheme()); Assertions.assertNull(builder.getAuthority()); Assertions.assertNull(builder.getPath()); Assertions.assertNull(builder.getHeaders()); Assertions.assertNull(builder.getVersion()); Assertions.assertNull(builder.getCharset()); Assertions.assertNull(builder.getParameters()); final BasicHttpRequest r1 = builder.build(); Assertions.assertNotNull(r1); Assertions.assertEquals("GET", r1.getMethod()); Assertions.assertNull(r1.getScheme()); Assertions.assertNull(r1.getAuthority()); Assertions.assertNull(r1.getPath()); Assertions.assertEquals(URI.create("/"), r1.getUri()); Assertions.assertNull(r1.getVersion()); builder.setUri(URI.create("http://host:1234/blah?param=value")); builder.setVersion(HttpVersion.HTTP_1_1); Assertions.assertEquals("http", builder.getScheme()); Assertions.assertEquals(new URIAuthority("host", 1234), builder.getAuthority()); Assertions.assertEquals("/blah?param=value", builder.getPath()); Assertions.assertEquals(URI.create("http://host:1234/blah?param=value"), builder.getUri()); Assertions.assertEquals(HttpVersion.HTTP_1_1, builder.getVersion()); final BasicHttpRequest r2 = builder.build(); Assertions.assertEquals("GET", r2.getMethod()); Assertions.assertEquals("http", r2.getScheme()); Assertions.assertEquals(new URIAuthority("host", 1234), r2.getAuthority()); Assertions.assertEquals("/blah?param=value", r2.getPath()); Assertions.assertEquals(URI.create("http://host:1234/blah?param=value"), r2.getUri()); Assertions.assertEquals(HttpVersion.HTTP_1_1, builder.getVersion()); builder.setCharset(StandardCharsets.US_ASCII); builder.addParameter("param1", "value1"); builder.addParameter("param2", null); builder.addParameters(new BasicNameValuePair("param3", "value3"), new BasicNameValuePair("param4", null)); Assertions.assertEquals(builder.getParameters(), Arrays.asList( new BasicNameValuePair("param1", "value1"), new BasicNameValuePair("param2", null), new BasicNameValuePair("param3", "value3"), new BasicNameValuePair("param4", null) )); Assertions.assertEquals(URI.create("http://host:1234/blah?param=value"), builder.getUri()); final BasicHttpRequest r3 = builder.build(); Assertions.assertEquals("GET", r3.getMethod()); Assertions.assertEquals("http", r3.getScheme()); Assertions.assertEquals(new URIAuthority("host", 1234), r3.getAuthority()); Assertions.assertEquals("/blah?param=value¶m1=value1¶m2¶m3=value3¶m4", r3.getPath()); Assertions.assertEquals(URI.create("http://host:1234/blah?param=value¶m1=value1¶m2¶m3=value3¶m4"), r3.getUri()); builder.addHeader("h1", "v1"); builder.addHeader("h1", "v2"); builder.addHeader("h2", "v2"); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); assertThat(builder.getHeaders("h1"), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"))); assertThat(builder.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1")); assertThat(builder.getLastHeader("h1"), HeaderMatcher.same("h1", "v2")); final BasicHttpRequest r4 = builder.build(); assertThat(r4.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); assertThat(r4.getHeaders("h1"), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"))); assertThat(r4.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1")); assertThat(r4.getLastHeader("h1"), HeaderMatcher.same("h1", "v2")); builder.removeHeader(new BasicHeader("h1", "v2")); assertThat(builder.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1"))); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2"))); final BasicHttpRequest r5 = builder.build(); assertThat(r5.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1"))); assertThat(r5.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2"))); builder.removeHeaders("h1"); assertThat(builder.getHeaders("h1"), HeadersMatcher.same()); assertThat(builder.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2"))); final BasicHttpRequest r6 = builder.build(); assertThat(r6.getHeaders("h1"), HeadersMatcher.same()); assertThat(r6.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2"))); } @Test public void testResponseCopy() throws Exception { final HttpResponse response = new BasicHttpResponse(400); response.addHeader("h1", "v1"); response.addHeader("h1", "v2"); response.addHeader("h2", "v2"); response.setVersion(HttpVersion.HTTP_2); final BasicResponseBuilder builder = BasicResponseBuilder.copy(response); Assertions.assertEquals(400, builder.getStatus()); Assertions.assertEquals(HttpVersion.HTTP_2, builder.getVersion()); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); } @Test public void testRequestCopy() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, URI.create("https://host:3456/stuff?blah")) ; request.addHeader("h1", "v1"); request.addHeader("h1", "v2"); request.addHeader("h2", "v2"); request.setVersion(HttpVersion.HTTP_2); final BasicRequestBuilder builder = BasicRequestBuilder.copy(request); Assertions.assertEquals("GET", builder.getMethod()); Assertions.assertEquals("https", builder.getScheme()); Assertions.assertEquals(new URIAuthority("host", 3456), builder.getAuthority()); Assertions.assertEquals("/stuff?blah", builder.getPath()); Assertions.assertEquals(HttpVersion.HTTP_2, builder.getVersion()); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/0040775 0000000 0000000 00000000000 14435411677 022254 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicHeaderIterator.java0100664 0000000 0000000 00000025202 14403631147 027610 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.Iterator; import java.util.NoSuchElementException; import org.apache.hc.core5.http.Header; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests for {@link java.util.Iterator} of {@link org.apache.hc.core5.http.Header}s. * */ public class TestBasicHeaderIterator { @Test public void testAllSame() { final Header[] headers = new Header[]{ new BasicHeader("Name", "value0"), new BasicHeader("nAme", "value1, value1.1"), new BasicHeader("naMe", "value2=whatever"), new BasicHeader("namE", "value3;tag=nil"), }; // without filter Iterator
hit = new BasicHeaderIterator(headers, null); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[0], hit.next(), "0"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[1], hit.next(), "1"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[2], hit.next(), "2"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[3], hit.next(), "3"); Assertions.assertFalse(hit.hasNext()); // with filter hit = new BasicHeaderIterator(headers, "name"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[0], hit.next(), "0"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[1], hit.next(), "1"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[2], hit.next(), "2"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[3], hit.next(), "3"); Assertions.assertFalse(hit.hasNext()); } @Test public void testFirstLastOneNone() { final Header[] headers = new Header[]{ new BasicHeader("match" , "value0"), new BasicHeader("mismatch", "value1, value1.1"), new BasicHeader("single" , "value2=whatever"), new BasicHeader("match" , "value3;tag=nil"), }; // without filter Iterator
hit = new BasicHeaderIterator(headers, null); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[0], hit.next(), "0"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[1], hit.next(), "1"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[2], hit.next(), "2"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[3], hit.next(), "3"); Assertions.assertFalse(hit.hasNext()); // with filter, first & last hit = new BasicHeaderIterator(headers, "match"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[0], hit.next()); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[3], hit.next()); Assertions.assertFalse(hit.hasNext()); // with filter, one match hit = new BasicHeaderIterator(headers, "single"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[2], hit.next()); Assertions.assertFalse(hit.hasNext()); // with filter, no match hit = new BasicHeaderIterator(headers, "way-off"); Assertions.assertFalse(hit.hasNext()); } @Test public void testInterspersed() { final Header[] headers = new Header[]{ new BasicHeader("yellow", "00"), new BasicHeader("maroon", "01"), new BasicHeader("orange", "02"), new BasicHeader("orange", "03"), new BasicHeader("orange", "04"), new BasicHeader("yellow", "05"), new BasicHeader("maroon", "06"), new BasicHeader("maroon", "07"), new BasicHeader("maroon", "08"), new BasicHeader("yellow", "09"), new BasicHeader("maroon", "0a"), new BasicHeader("yellow", "0b"), new BasicHeader("orange", "0c"), new BasicHeader("yellow", "0d"), new BasicHeader("orange", "0e"), }; // without filter Iterator
hit = new BasicHeaderIterator(headers, null); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[0], hit.next(), "0"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[1], hit.next(), "1"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[2], hit.next(), "2"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[3], hit.next(), "3"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[4], hit.next(), "4"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[5], hit.next(), "5"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[6], hit.next(), "6"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[7], hit.next(), "7"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[8], hit.next(), "8"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[9], hit.next(), "9"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[10], hit.next(), "a"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[11], hit.next(), "b"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[12], hit.next(), "c"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[13], hit.next(), "d"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[14], hit.next(), "e"); Assertions.assertFalse(hit.hasNext()); // yellow 0, 5, 9, 11, 13 hit = new BasicHeaderIterator(headers, "Yellow"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[0], hit.next()); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[5], hit.next(), "5"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[9], hit.next(), "9"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[11], hit.next(), "b"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[13], hit.next(), "d"); Assertions.assertFalse(hit.hasNext()); // maroon 1, 6, 7, 8, 10 hit = new BasicHeaderIterator(headers, "marOOn"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[1], hit.next(),"1"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[6], hit.next(),"6"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[7], hit.next(), "7"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[8], hit.next(), "8"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[10], hit.next(), "a"); Assertions.assertFalse(hit.hasNext()); // orange 2, 3, 4, 12, 14 hit = new BasicHeaderIterator(headers, "OranGe"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[2], hit.next()); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[3], hit.next()); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[4], hit.next(), "4"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[12], hit.next(), "b"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[14], hit.next(), "e"); Assertions.assertFalse(hit.hasNext()); } @Test public void testInvalid() { Assertions.assertThrows(NullPointerException.class, () -> new BasicHeaderIterator(null, "whatever")); // this is not invalid final Iterator
hit = new BasicHeaderIterator(new Header[0], "whatever"); Assertions.assertFalse(hit.hasNext()); // but this is Assertions.assertThrows(NoSuchElementException.class, () -> hit.next()); } @Test public void testRemaining() { // to satisfy Clover and take coverage to 100% final Header[] headers = new Header[]{ new BasicHeader("Name", "value0"), new BasicHeader("nAme", "value1, value1.1"), new BasicHeader("naMe", "value2=whatever"), new BasicHeader("namE", "value3;tag=nil"), }; // without filter, using plain next() final Iterator
hit = new BasicHeaderIterator(headers, null); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[0], hit.next(), "0"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[1], hit.next(), "1"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[2], hit.next(), "2"); Assertions.assertTrue(hit.hasNext()); Assertions.assertEquals(headers[3], hit.next(), "3"); Assertions.assertFalse(hit.hasNext()); final Iterator
hit2 = new BasicHeaderIterator(headers, null); Assertions.assertTrue(hit2.hasNext()); Assertions.assertThrows(UnsupportedOperationException.class, () -> hit2.remove()); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicLineFormatter.java0100664 0000000 0000000 00000012660 14403631147 027465 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests for {@link BasicLineFormatter}. */ public class TestBasicLineFormatter { private BasicLineFormatter formatter; @BeforeEach public void setup() { this.formatter = BasicLineFormatter.INSTANCE; } @Test public void testHttpVersionFormatting() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); this.formatter.formatProtocolVersion(buf, HttpVersion.HTTP_1_1); Assertions.assertEquals("HTTP/1.1", buf.toString()); } @Test public void testRLFormatting() throws Exception { final RequestLine requestline = new RequestLine(Method.GET.name(), "/stuff", HttpVersion.HTTP_1_1); final CharArrayBuffer buf = new CharArrayBuffer(64); this.formatter.formatRequestLine(buf, requestline); Assertions.assertEquals("GET /stuff HTTP/1.1", buf.toString()); } @Test public void testRLFormattingInvalidInput() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); final RequestLine requestline = new RequestLine(Method.GET.name(), "/stuff", HttpVersion.HTTP_1_1); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatRequestLine(null, requestline)); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatRequestLine(buf, null)); } @Test public void testSLFormatting() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); final StatusLine statusline1 = new StatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); this.formatter.formatStatusLine(buf, statusline1); Assertions.assertEquals("HTTP/1.1 200 OK", buf.toString()); buf.clear(); final StatusLine statusline2 = new StatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, null); this.formatter.formatStatusLine(buf, statusline2); Assertions.assertEquals("HTTP/1.1 200 ", buf.toString()); // see "testSLParseSuccess" in TestBasicLineParser: // trailing space is correct } @Test public void testSLFormattingInvalidInput() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); final StatusLine statusline = new StatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatStatusLine(null, statusline)); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatStatusLine(buf, null)); } @Test public void testHeaderFormatting() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); final Header header1 = new BasicHeader("name", "value"); this.formatter.formatHeader(buf, header1); Assertions.assertEquals("name: value", buf.toString()); buf.clear(); final Header header2 = new BasicHeader("name", null); this.formatter.formatHeader(buf, header2); Assertions.assertEquals("name: ", buf.toString()); } @Test public void testHeaderFormattingInvalidInput() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); final Header header = new BasicHeader("name", "value"); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatHeader(null, header)); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatHeader(buf, null)); } @Test public void testHeaderFormattingRequestSplitting() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); final Header header = new BasicHeader("Host", "apache.org\r\nOops: oops"); formatter.formatHeader(buf, header); final String s = buf.toString(); Assertions.assertFalse(s.contains("\n")); Assertions.assertEquals("Host: apache.org Oops: oops", s); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicHeaderValueFormatter.java0100664 0000000 0000000 00000022524 14403631147 030763 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests for header value formatting. * * */ public class TestBasicHeaderValueFormatter { private BasicHeaderValueFormatter formatter; @BeforeEach public void setup() { this.formatter = BasicHeaderValueFormatter.INSTANCE; } @Test public void testNVPFormatting() throws Exception { final NameValuePair param1 = new BasicNameValuePair("param", "regular_stuff"); final NameValuePair param2 = new BasicNameValuePair("param", "this\\that"); final NameValuePair param3 = new BasicNameValuePair("param", "this,that"); final NameValuePair param4 = new BasicNameValuePair("param", "quote marks (\") must be escaped"); final NameValuePair param5 = new BasicNameValuePair("param", "back slash (\\) must be escaped too"); final NameValuePair param6 = new BasicNameValuePair("param", "values with\tblanks must always be quoted"); final NameValuePair param7 = new BasicNameValuePair("param", null); final CharArrayBuffer buf = new CharArrayBuffer(64); buf.clear(); this.formatter.formatNameValuePair(buf, param1, false); Assertions.assertEquals("param=regular_stuff", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param2, false); Assertions.assertEquals("param=\"this\\\\that\"", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param3, false); Assertions.assertEquals("param=\"this,that\"", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param4, false); Assertions.assertEquals("param=\"quote marks (\\\") must be escaped\"", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param5, false); Assertions.assertEquals("param=\"back slash (\\\\) must be escaped too\"", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param6, false); Assertions.assertEquals("param=\"values with\tblanks must always be quoted\"", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param7, false); Assertions.assertEquals("param", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param1, true); Assertions.assertEquals("param=\"regular_stuff\"", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param2, true); Assertions.assertEquals("param=\"this\\\\that\"", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param3, true); Assertions.assertEquals("param=\"this,that\"", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param4, true); Assertions.assertEquals("param=\"quote marks (\\\") must be escaped\"", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param5, true); Assertions.assertEquals("param=\"back slash (\\\\) must be escaped too\"", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param6, true); Assertions.assertEquals("param=\"values with\tblanks must always be quoted\"", buf.toString()); buf.clear(); this.formatter.formatNameValuePair(buf, param7, true); Assertions.assertEquals("param", buf.toString()); } @Test public void testParamsFormatting() throws Exception { final NameValuePair param1 = new BasicNameValuePair("param", "regular_stuff"); final NameValuePair param2 = new BasicNameValuePair("param", "this\\that"); final NameValuePair param3 = new BasicNameValuePair("param", "this,that"); final NameValuePair[] params = new NameValuePair[] {param1, param2, param3}; final CharArrayBuffer buf = new CharArrayBuffer(64); buf.clear(); this.formatter.formatParameters(buf, params, false); Assertions.assertEquals("param=regular_stuff; param=\"this\\\\that\"; param=\"this,that\"", buf.toString()); buf.clear(); this.formatter.formatParameters(buf, params, true); Assertions.assertEquals("param=\"regular_stuff\"; param=\"this\\\\that\"; param=\"this,that\"", buf.toString()); } @Test public void testHEFormatting() throws Exception { final NameValuePair param1 = new BasicNameValuePair("param", "regular_stuff"); final NameValuePair param2 = new BasicNameValuePair("param", "this\\that"); final NameValuePair param3 = new BasicNameValuePair("param", "this,that"); final NameValuePair param4 = new BasicNameValuePair("param", null); final NameValuePair[] params = new NameValuePair[] {param1, param2, param3, param4}; final HeaderElement element = new BasicHeaderElement("name", "value", params); final CharArrayBuffer buf = new CharArrayBuffer(64); this.formatter.formatHeaderElement(buf, element, false); Assertions.assertEquals("name=value; param=regular_stuff; param=\"this\\\\that\"; param=\"this,that\"; param", buf.toString()); } @Test public void testElementsFormatting() throws Exception { final NameValuePair param1 = new BasicNameValuePair("param", "regular_stuff"); final NameValuePair param2 = new BasicNameValuePair("param", "this\\that"); final NameValuePair param3 = new BasicNameValuePair("param", "this,that"); final NameValuePair param4 = new BasicNameValuePair("param", null); final HeaderElement element1 = new BasicHeaderElement("name1", "value1", new NameValuePair[] {param1}); final HeaderElement element2 = new BasicHeaderElement("name2", "value2", new NameValuePair[] {param2}); final HeaderElement element3 = new BasicHeaderElement("name3", "value3", new NameValuePair[] {param3}); final HeaderElement element4 = new BasicHeaderElement("name4", "value4", new NameValuePair[] {param4}); final HeaderElement element5 = new BasicHeaderElement("name5", null); final HeaderElement[] elements = new HeaderElement[] {element1, element2, element3, element4, element5}; final CharArrayBuffer buf = new CharArrayBuffer(64); this.formatter.formatElements(buf, elements, false); Assertions.assertEquals("name1=value1; param=regular_stuff, name2=value2; " + "param=\"this\\\\that\", name3=value3; param=\"this,that\", " + "name4=value4; param, name5", buf.toString()); } @Test public void testInvalidArguments() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); final NameValuePair param = new BasicNameValuePair("param", "regular_stuff"); final NameValuePair[] params = new NameValuePair[] {param}; final HeaderElement element = new BasicHeaderElement("name1", "value1", null); final HeaderElement[] elements = new HeaderElement[] {element}; Assertions.assertThrows(NullPointerException.class, () -> formatter.formatNameValuePair(null, param, false)); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatNameValuePair(buf, null, false)); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatParameters(null, params, false)); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatParameters(buf, null, false)); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatHeaderElement(null, element, false)); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatHeaderElement(buf, null, false)); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatElements(null, elements, false)); Assertions.assertThrows(NullPointerException.class, () -> formatter.formatElements(buf, null, false)); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestMessageSupport.java0100664 0000000 0000000 00000013152 14403631147 026726 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.io.entity.HttpEntities; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMessageSupport { private static Set makeSet(final String... tokens) { if (tokens == null) { return null; } final Set set = new LinkedHashSet<>(); Collections.addAll(set, tokens); return set; } @Test public void testTokenSetFormatting() throws Exception { final Header header = MessageSupport.format(HttpHeaders.TRAILER, makeSet("z", "b", "a")); Assertions.assertNotNull(header); Assertions.assertEquals("z, b, a", header.getValue()); } @Test public void testTokenSetFormattingSameName() throws Exception { final Header header = MessageSupport.format(HttpHeaders.TRAILER, makeSet("a", "a", "a")); Assertions.assertNotNull(header); Assertions.assertEquals("a", header.getValue()); } @Test public void testTokensFormattingSameName() throws Exception { final Header header = MessageSupport.format(HttpHeaders.TRAILER, "a", "a", "a"); Assertions.assertNotNull(header); Assertions.assertEquals("a, a, a", header.getValue()); } @Test public void testTrailerNoTrailers() throws Exception { final Header header = MessageSupport.format(HttpHeaders.TRAILER); Assertions.assertNull(header); } @Test public void testParseTokens() throws Exception { final String s = "a, b, c, c"; final ParserCursor cursor = new ParserCursor(0, s.length()); Assertions.assertEquals(makeSet("a", "b", "c"), MessageSupport.parseTokens(s, cursor)); } @Test public void testParseTokenHeader() throws Exception { final Header header = new BasicHeader(HttpHeaders.TRAILER, "a, b, c, c"); Assertions.assertEquals(makeSet("a", "b", "c"), MessageSupport.parseTokens(header)); } @Test public void testParseTokenBufferedHeader() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(128); buf.append("stuff: a, b, c, c"); final Header header = BufferedHeader.create(buf); Assertions.assertEquals(makeSet("a", "b", "c"), MessageSupport.parseTokens(header)); } @Test public void testAddContentHeaders() throws Exception { final HttpEntity entity = HttpEntities.create("some stuff with trailers", StandardCharsets.US_ASCII, new BasicHeader("z", "this"), new BasicHeader("b", "that"), new BasicHeader("a", "this and that")); final HttpMessage message = new BasicHttpResponse(200); MessageSupport.addTrailerHeader(message, entity); MessageSupport.addContentTypeHeader(message, entity); final Header h1 = message.getFirstHeader(HttpHeaders.TRAILER); final Header h2 = message.getFirstHeader(HttpHeaders.CONTENT_TYPE); Assertions.assertNotNull(h1); Assertions.assertEquals("z, b, a", h1.getValue()); Assertions.assertNotNull(h2); Assertions.assertEquals("text/plain; charset=US-ASCII", h2.getValue()); } @Test public void testContentHeadersAlreadyPresent() throws Exception { final HttpEntity entity = HttpEntities.create("some stuff with trailers", StandardCharsets.US_ASCII, new BasicHeader("z", "this"), new BasicHeader("b", "that"), new BasicHeader("a", "this and that")); final HttpMessage message = new BasicHttpResponse(200); message.addHeader(HttpHeaders.TRAILER, "a, a, a"); message.addHeader(HttpHeaders.CONTENT_TYPE, "text/plain; charset=ascii"); MessageSupport.addTrailerHeader(message, entity); MessageSupport.addContentTypeHeader(message, entity); final Header h1 = message.getFirstHeader(HttpHeaders.TRAILER); final Header h2 = message.getFirstHeader(HttpHeaders.CONTENT_TYPE); Assertions.assertNotNull(h1); Assertions.assertEquals("a, a, a", h1.getValue()); Assertions.assertNotNull(h2); Assertions.assertEquals("text/plain; charset=ascii", h2.getValue()); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestHeaderElement.java0100664 0000000 0000000 00000007530 14403631147 026452 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for {@link HeaderElement}. */ public class TestHeaderElement { @Test public void testConstructor3() throws Exception { final HeaderElement element = new BasicHeaderElement("name", "value", new NameValuePair[] { new BasicNameValuePair("param1", "value1"), new BasicNameValuePair("param2", "value2") } ); Assertions.assertEquals("name", element.getName()); Assertions.assertEquals("value", element.getValue()); Assertions.assertEquals(2, element.getParameters().length); Assertions.assertEquals("value1", element.getParameterByName("param1").getValue()); Assertions.assertEquals("value2", element.getParameterByName("param2").getValue()); } @Test public void testConstructor2() throws Exception { final HeaderElement element = new BasicHeaderElement("name", "value"); Assertions.assertEquals("name", element.getName()); Assertions.assertEquals("value", element.getValue()); Assertions.assertEquals(0, element.getParameters().length); } @Test public void testInvalidName() { Assertions.assertThrows(NullPointerException.class, () -> new BasicHeaderElement(null, null, null)); } @Test public void testParamByName() throws Exception { final String s = "name = value; param1 = value1; param2 = value2"; final CharArrayBuffer buf = new CharArrayBuffer(64); buf.append(s); final ParserCursor cursor = new ParserCursor(0, buf.length()); final HeaderElement element = BasicHeaderValueParser.INSTANCE.parseHeaderElement(buf, cursor); Assertions.assertEquals("value1", element.getParameterByName("param1").getValue()); Assertions.assertEquals("value2", element.getParameterByName("param2").getValue()); Assertions.assertNull(element.getParameterByName("param3")); Assertions.assertThrows(NullPointerException.class, () -> element.getParameterByName(null)); } @Test public void testToString() { final BasicHeaderElement element = new BasicHeaderElement("name", "value", new NameValuePair[] { new BasicNameValuePair("param1", "value1"), new BasicNameValuePair("param2", "value2") } ); Assertions.assertEquals("name=value; param1=value1; param2=value2", element.toString()); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/HttpResponseWrapperTest.java0100664 0000000 0000000 00000007674 14403631147 027760 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.Locale; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class HttpResponseWrapperTest { @Test void testDefaultResponseConstructors() { final HttpResponse response1 = new BasicHttpResponse(HttpStatus.SC_BAD_REQUEST, "Bad Request"); final HttpResponseWrapper httpResponseWrapper1 = new HttpResponseWrapper(response1); Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST, httpResponseWrapper1.getCode()); final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "whatever"); final HttpResponseWrapper httpResponseWrapper2 = new HttpResponseWrapper(response2); Assertions.assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, httpResponseWrapper2.getCode()); Assertions.assertEquals("whatever", httpResponseWrapper2.getReasonPhrase()); httpResponseWrapper2.setReasonPhrase("another-whatever"); Assertions.assertEquals("another-whatever", httpResponseWrapper2.getReasonPhrase()); } @Test void testSetResponseStatus() { final HttpResponse response1 = new BasicHttpResponse(200, "OK"); final HttpResponseWrapper httpResponseWrapper1 = new HttpResponseWrapper(response1); Assertions.assertNotNull(httpResponseWrapper1.getCode()); Assertions.assertEquals(200, httpResponseWrapper1.getCode()); final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_BAD_REQUEST, "Bad Request"); final HttpResponseWrapper httpResponseWrapper2 = new HttpResponseWrapper(response2); Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST, httpResponseWrapper2.getCode()); final HttpResponse response3 = new BasicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "whatever"); final HttpResponseWrapper httpResponseWrapper3 = new HttpResponseWrapper(response3); Assertions.assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, httpResponseWrapper3.getCode()); Assertions.assertEquals("whatever", httpResponseWrapper3.getReasonPhrase()); final HttpResponse response4 = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); final HttpResponseWrapper httpResponseWrapper4 = new HttpResponseWrapper(response4); Assertions.assertThrows(IllegalArgumentException.class, () -> httpResponseWrapper4.setCode(-23)); } @Test void testLocale() { final HttpResponse response = new BasicHttpResponse(200, "OK"); final HttpResponseWrapper httpResponseWrapper = new HttpResponseWrapper(response); httpResponseWrapper.setLocale(Locale.US); Assertions.assertEquals("US", httpResponseWrapper.getLocale().getCountry()); } }httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicStatusLine.java0100664 0000000 0000000 00000007414 14403631147 027006 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.message.StatusLine.StatusClass; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link org.apache.hc.core5.http.message.StatusLine}. */ public class TestBasicStatusLine { @Test public void testGetStatusClass() { StatusLine statusLine = new StatusLine(new BasicHttpResponse(100, "Continue")); Assertions.assertEquals(StatusClass.INFORMATIONAL, statusLine.getStatusClass()); statusLine = new StatusLine(new BasicHttpResponse(200, "OK")); Assertions.assertEquals(StatusClass.SUCCESSFUL, statusLine.getStatusClass()); statusLine = new StatusLine(new BasicHttpResponse(302, "Found")); Assertions.assertEquals(StatusClass.REDIRECTION, statusLine.getStatusClass()); statusLine = new StatusLine(new BasicHttpResponse(409, "Conflict")); Assertions.assertEquals(StatusClass.CLIENT_ERROR, statusLine.getStatusClass()); statusLine = new StatusLine(new BasicHttpResponse(502, "Bad Gateway")); Assertions.assertEquals(StatusClass.SERVER_ERROR, statusLine.getStatusClass()); statusLine = new StatusLine(new BasicHttpResponse(999, "Not a status")); Assertions.assertEquals(StatusClass.OTHER, statusLine.getStatusClass()); } @Test public void testGetStatusShorthand() { StatusLine statusLine = new StatusLine(new BasicHttpResponse(100, "Continue")); Assertions.assertTrue(statusLine.isInformational()); Assertions.assertFalse(statusLine.isSuccessful()); Assertions.assertFalse(statusLine.isError()); statusLine = new StatusLine(new BasicHttpResponse(200, "OK")); Assertions.assertTrue(statusLine.isSuccessful()); Assertions.assertFalse(statusLine.isRedirection()); Assertions.assertFalse(statusLine.isError()); statusLine = new StatusLine(new BasicHttpResponse(302, "Found")); Assertions.assertTrue(statusLine.isRedirection()); Assertions.assertFalse(statusLine.isClientError()); Assertions.assertFalse(statusLine.isError()); statusLine = new StatusLine(new BasicHttpResponse(409, "Conflict")); Assertions.assertTrue(statusLine.isClientError()); Assertions.assertTrue(statusLine.isError()); Assertions.assertFalse(statusLine.isServerError()); statusLine = new StatusLine(new BasicHttpResponse(502, "Bad Gateway")); Assertions.assertTrue(statusLine.isServerError()); Assertions.assertTrue(statusLine.isError()); Assertions.assertFalse(statusLine.isSuccessful()); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestHeaderGroup.java0100664 0000000 0000000 00000022412 14403631147 026151 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Iterator; import org.apache.hc.core5.http.Header; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link HeaderGroup}. * */ public class TestHeaderGroup { @Test public void testConstructor() { final HeaderGroup headergroup = new HeaderGroup(); Assertions.assertNotNull(headergroup.getHeaders()); Assertions.assertEquals(0, headergroup.getHeaders().length); } @Test public void testClear() { final HeaderGroup headergroup = new HeaderGroup(); headergroup.addHeader(new BasicHeader("name", "value")); Assertions.assertEquals(1, headergroup.getHeaders().length); headergroup.clear(); Assertions.assertEquals(0, headergroup.getHeaders().length); } @Test public void testAddRemoveHeader() { final HeaderGroup headerGroup = new HeaderGroup(); final Header header = new BasicHeader("name", "value"); headerGroup.addHeader(header); headerGroup.addHeader(null); Assertions.assertEquals(1, headerGroup.getHeaders().length); Assertions.assertTrue(headerGroup.removeHeader(header)); Assertions.assertFalse(headerGroup.removeHeader(null)); Assertions.assertEquals(0, headerGroup.getHeaders().length); } @Test public void testAddRemoveHeaders() { final HeaderGroup headergroup = new HeaderGroup(); final Header header = new BasicHeader("name", "value"); headergroup.addHeader(header); headergroup.addHeader(header); Assertions.assertEquals(2, headergroup.getHeaders().length); Assertions.assertFalse(headergroup.removeHeaders((Header) null)); Assertions.assertTrue(headergroup.removeHeaders(header)); Assertions.assertFalse(headergroup.removeHeaders((Header) null)); Assertions.assertEquals(0, headergroup.getHeaders().length); } @Test public void testAddRemoveHeaderWithDifferentButEqualHeaders() { final HeaderGroup headergroup = new HeaderGroup(); final Header header = new BasicHeader("name", "value"); final Header header2 = new BasicHeader("name", "value"); headergroup.addHeader(header); Assertions.assertEquals(1, headergroup.getHeaders().length); Assertions.assertTrue(headergroup.removeHeader(header2)); Assertions.assertEquals(0, headergroup.getHeaders().length); } @Test public void testUpdateHeader() { final HeaderGroup headergroup = new HeaderGroup(); final Header header1 = new BasicHeader("name1", "value1"); final Header header2 = new BasicHeader("name2", "value2"); final Header header3 = new BasicHeader("name3", "value3"); headergroup.addHeader(header1); headergroup.addHeader(header2); headergroup.addHeader(header3); headergroup.setHeader(new BasicHeader("name2", "newvalue")); headergroup.setHeader(new BasicHeader("name4", "value4")); headergroup.setHeader(null); Assertions.assertEquals(4, headergroup.getHeaders().length); Assertions.assertEquals("newvalue", headergroup.getFirstHeader("name2").getValue()); } @Test public void testSetHeaders() { final HeaderGroup headergroup = new HeaderGroup(); final Header header1 = new BasicHeader("name1", "value1"); final Header header2 = new BasicHeader("name2", "value2"); final Header header3 = new BasicHeader("name3", "value3"); headergroup.addHeader(header1); headergroup.setHeaders(header2, header3); Assertions.assertEquals(2, headergroup.getHeaders().length); Assertions.assertEquals(0, headergroup.getHeaders("name1").length); Assertions.assertFalse(headergroup.containsHeader("name1")); Assertions.assertEquals(1, headergroup.getHeaders("name2").length); Assertions.assertTrue(headergroup.containsHeader("name2")); Assertions.assertEquals(1, headergroup.getHeaders("name3").length); Assertions.assertTrue(headergroup.containsHeader("name3")); headergroup.setHeaders(); Assertions.assertEquals(0, headergroup.getHeaders().length); } @Test public void testFirstLastHeaders() { final HeaderGroup headergroup = new HeaderGroup(); final Header header1 = new BasicHeader("name", "value1"); final Header header2 = new BasicHeader("name", "value2"); final Header header3 = new BasicHeader("name", "value3"); headergroup.setHeaders(header1, header2, header3); Assertions.assertNull(headergroup.getFirstHeader("whatever")); Assertions.assertNull(headergroup.getLastHeader("whatever")); Assertions.assertEquals("value1", headergroup.getFirstHeader("name").getValue()); Assertions.assertEquals("value3", headergroup.getLastHeader("name").getValue()); } @Test public void testCondensedHeader() { final HeaderGroup headergroup = new HeaderGroup(); Assertions.assertNull(headergroup.getCondensedHeader("name")); final Header header1 = new BasicHeader("name", "value1"); final Header header2 = new BasicHeader("name", "value2"); final Header header3 = new BasicHeader("name", "value3"); headergroup.setHeaders(header1, header2, header3); Assertions.assertEquals("value1, value2, value3", headergroup.getCondensedHeader("name").getValue()); headergroup.setHeaders(header1); Assertions.assertEquals(header1, headergroup.getCondensedHeader("name")); } @Test public void testIterator() { final HeaderGroup headergroup = new HeaderGroup(); final Iterator
i = headergroup.headerIterator(); Assertions.assertNotNull(i); Assertions.assertFalse(i.hasNext()); } @Test public void testHeaderRemove() { final HeaderGroup headergroup = new HeaderGroup(); final Header header1 = new BasicHeader("name", "value1"); final Header header2 = new BasicHeader("name", "value2"); final Header header3 = new BasicHeader("name", "value3"); headergroup.setHeaders(header1, header2, header3); final Iterator
i = headergroup.headerIterator(); Assertions.assertNotNull(i); Assertions.assertTrue(i.hasNext()); i.next(); Assertions.assertTrue(i.hasNext()); i.next(); i.remove(); Assertions.assertEquals(2, headergroup.getHeaders().length); Assertions.assertTrue(i.hasNext()); i.next(); i.remove(); Assertions.assertEquals(1, headergroup.getHeaders().length); Assertions.assertFalse(i.hasNext()); } @Test public void testSerialization() throws Exception { final HeaderGroup orig = new HeaderGroup(); final Header header1 = new BasicHeader("name", "value1"); final Header header2 = new BasicHeader("name", "value2"); final Header header3 = new BasicHeader("name", "value3"); orig.setHeaders(header1, header2, header3); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer); outStream.writeObject(orig); outStream.close(); final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final HeaderGroup clone = (HeaderGroup) inStream.readObject(); final Header[] headers1 = orig.getHeaders(); final Header[] headers2 = clone.getHeaders(); Assertions.assertNotNull(headers1); Assertions.assertNotNull(headers2); Assertions.assertEquals(headers1.length, headers2.length); for (int i = 0; i < headers1.length; i++) { Assertions.assertEquals(headers1[i].getName(), headers2[i].getName()); Assertions.assertEquals(headers1[i].getValue(), headers2[i].getValue()); } } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBufferedHeader.java0100664 0000000 0000000 00000010036 14403631147 026576 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link BufferedHeader}. * */ public class TestBufferedHeader { @Test public void testBasicConstructor() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(32); buf.append("name: value"); final BufferedHeader header = new BufferedHeader(buf, false); Assertions.assertEquals("name", header.getName()); Assertions.assertEquals("value", header.getValue()); Assertions.assertSame(buf, header.getBuffer()); Assertions.assertEquals(5, header.getValuePos()); } @Test public void testSerialization() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(32); buf.append("name: value"); final BufferedHeader orig = new BufferedHeader(buf, false); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer); outStream.writeObject(orig); outStream.close(); final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final BufferedHeader clone = (BufferedHeader) inStream.readObject(); Assertions.assertEquals(orig.getName(), clone.getName()); Assertions.assertEquals(orig.getValue(), clone.getValue()); } @Test public void testInvalidHeaderParsing() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(16); buf.clear(); buf.append(""); Assertions.assertThrows(ParseException.class, () -> new BufferedHeader(buf, false)); buf.clear(); buf.append("blah"); Assertions.assertThrows(ParseException.class, () -> new BufferedHeader(buf, false)); buf.clear(); buf.append(":"); Assertions.assertThrows(ParseException.class, () -> new BufferedHeader(buf, false)); buf.clear(); buf.append(" :"); Assertions.assertThrows(ParseException.class, () -> new BufferedHeader(buf, false)); buf.clear(); buf.append(": blah"); Assertions.assertThrows(ParseException.class, () -> new BufferedHeader(buf, false)); buf.clear(); buf.append(" : blah"); Assertions.assertThrows(ParseException.class, () -> new BufferedHeader(buf, false)); buf.clear(); buf.append("header : blah"); Assertions.assertThrows(ParseException.class, () -> new BufferedHeader(buf, true)); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestHeader.java0100664 0000000 0000000 00000007010 14403631147 025131 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.apache.hc.core5.http.Header; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link Header}. */ public class TestHeader { @Test public void testBasicConstructor() { final Header header = new BasicHeader("name", "value"); Assertions.assertEquals("name", header.getName()); Assertions.assertEquals("value", header.getValue()); } @Test public void testBasicConstructorNullValue() { final Header header = new BasicHeader("name", null); Assertions.assertEquals("name", header.getName()); Assertions.assertNull(header.getValue()); } @Test public void testInvalidName() { Assertions.assertThrows(NullPointerException.class, () -> new BasicHeader(null, null)); } @Test public void testToString() { final Header header1 = new BasicHeader("name1", "value1"); Assertions.assertEquals("name1: value1", header1.toString()); final Header header2 = new BasicHeader("name2", null); Assertions.assertEquals("name2: ", header2.toString()); } @Test public void testSerialization() throws Exception { final BasicHeader orig = new BasicHeader("name1", "value1"); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer); outStream.writeObject(orig); outStream.close(); final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final BasicHeader clone = (BasicHeader) inStream.readObject(); Assertions.assertEquals(orig.getName(), clone.getName()); Assertions.assertEquals(orig.getValue(), clone.getValue()); } @Test public void testClone() throws Exception { final BasicHeader orig = new BasicHeader("name1", "value1"); final BasicHeader clone = orig.clone(); Assertions.assertEquals(orig.getName(), clone.getName()); Assertions.assertEquals(orig.getValue(), clone.getValue()); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicTokenIterator.java0100664 0000000 0000000 00000016661 14403631147 027511 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.Iterator; import java.util.NoSuchElementException; import org.apache.hc.core5.http.Header; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests for {@link BasicTokenIterator}. * */ public class TestBasicTokenIterator { @Test public void testSingleHeader() { Header[] headers = new Header[]{ new BasicHeader("Name", "token0,token1, token2 , token3") }; Iterator
hit = new BasicHeaderIterator(headers, null); Iterator ti = new BasicTokenIterator(hit); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token0", "token0", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token1", "token1", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token2", "token2", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token3", "token3", ti.next()); Assertions.assertFalse(ti.hasNext()); headers = new Header[]{ new BasicHeader("Name", "token0") }; hit = new BasicHeaderIterator(headers, null); ti = new BasicTokenIterator(hit); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token0", "token0", ti.next()); Assertions.assertFalse(ti.hasNext()); } @Test public void testMultiHeader() { final Header[] headers = new Header[]{ new BasicHeader("Name", "token0,token1"), new BasicHeader("Name", ""), new BasicHeader("Name", "token2"), new BasicHeader("Name", " "), new BasicHeader("Name", "token3 "), new BasicHeader("Name", ","), new BasicHeader("Name", "token4"), }; final Iterator
hit = new BasicHeaderIterator(headers, null); final Iterator ti = new BasicTokenIterator(hit); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token0", "token0", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token1", "token1", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token2", "token2", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token3", "token3", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token4", "token4", ti.next()); Assertions.assertFalse(ti.hasNext()); } @Test public void testEmpty() { final Header[] headers = new Header[]{ new BasicHeader("Name", " "), new BasicHeader("Name", ""), new BasicHeader("Name", ","), new BasicHeader("Name", " ,, "), }; Iterator
hit = new BasicHeaderIterator(headers, null); Iterator ti = new BasicTokenIterator(hit); Assertions.assertFalse(ti.hasNext()); hit = new BasicHeaderIterator(headers, "empty"); ti = new BasicTokenIterator(hit); Assertions.assertFalse(ti.hasNext()); } @Test public void testValueStart() { final Header[] headers = new Header[]{ new BasicHeader("Name", "token0"), new BasicHeader("Name", " token1"), new BasicHeader("Name", ",token2"), new BasicHeader("Name", " ,token3"), new BasicHeader("Name", ", token4"), new BasicHeader("Name", " , token5"), }; final Iterator
hit = new BasicHeaderIterator(headers, null); final Iterator ti = new BasicTokenIterator(hit); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token0", "token0", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token1", "token1", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token2", "token2", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token3", "token3", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token4", "token4", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token5", "token5", ti.next()); Assertions.assertFalse(ti.hasNext()); } @Test public void testValueEnd() { final Header[] headers = new Header[]{ new BasicHeader("Name", "token0"), new BasicHeader("Name", "token1 "), new BasicHeader("Name", "token2,"), new BasicHeader("Name", "token3 ,"), new BasicHeader("Name", "token4, "), new BasicHeader("Name", "token5 , "), }; final Iterator
hit = new BasicHeaderIterator(headers, null); final Iterator ti = new BasicTokenIterator(hit); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token0", "token0", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token1", "token1", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token2", "token2", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token3", "token3", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token4", "token4", ti.next()); Assertions.assertTrue(ti.hasNext()); Assertions.assertEquals("token5", "token5", ti.next()); Assertions.assertFalse(ti.hasNext()); } @Test public void testWrongPublic() { Assertions.assertThrows(NullPointerException.class, () -> new BasicTokenIterator(null)); final Header[] headers = new Header[]{ new BasicHeader("Name", " "), new BasicHeader("Name", ""), new BasicHeader("Name", ","), new BasicHeader("Name", " ,, "), }; final Iterator
hit = new BasicHeaderIterator(headers, null); final Iterator ti = new BasicTokenIterator(hit); Assertions.assertThrows(NoSuchElementException.class, () -> ti.next()); Assertions.assertThrows(UnsupportedOperationException.class, () -> ti.remove()); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicHeaderElementIterator.java0100664 0000000 0000000 00000010246 14403631147 031124 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.util.Iterator; import java.util.NoSuchElementException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests for {@link BasicHeaderElementIterator}. * */ public class TestBasicHeaderElementIterator { @Test public void testMultiHeader() { final Header[] headers = new Header[]{ new BasicHeader("Name", "value0"), new BasicHeader("Name", "value1") }; final Iterator hei = new BasicHeaderElementIterator( new BasicHeaderIterator(headers, "Name")); Assertions.assertTrue(hei.hasNext()); HeaderElement elem = hei.next(); Assertions.assertEquals("value0", elem.getName(), "The two header values must be equal"); Assertions.assertTrue(hei.hasNext()); elem = hei.next(); Assertions.assertEquals("value1", elem.getName(), "The two header values must be equal"); Assertions.assertFalse(hei.hasNext()); Assertions.assertThrows(NoSuchElementException.class, () -> hei.next()); Assertions.assertFalse(hei.hasNext()); Assertions.assertThrows(NoSuchElementException.class, () -> hei.next()); } @Test public void testMultiHeaderSameLine() { final Header[] headers = new Header[]{ new BasicHeader("name", "value0,value1"), new BasicHeader("nAme", "cookie1=1,cookie2=2") }; final Iterator hei = new BasicHeaderElementIterator( new BasicHeaderIterator(headers, "Name")); HeaderElement elem = hei.next(); Assertions.assertEquals("value0", elem.getName(), "The two header values must be equal"); elem = hei.next(); Assertions.assertEquals("value1", elem.getName(), "The two header values must be equal"); elem = hei.next(); Assertions.assertEquals("cookie1", elem.getName(), "The two header values must be equal"); Assertions.assertEquals("1", elem.getValue(), "The two header values must be equal"); elem = hei.next(); Assertions.assertEquals("cookie2", elem.getName(), "The two header values must be equal"); Assertions.assertEquals("2", elem.getValue(), "The two header values must be equal"); } @Test public void testFringeCases() { final Header[] headers = new Header[]{ new BasicHeader("Name", null), new BasicHeader("Name", " "), new BasicHeader("Name", ",,,") }; final Iterator hei = new BasicHeaderElementIterator( new BasicHeaderIterator(headers, "Name")); Assertions.assertFalse(hei.hasNext()); Assertions.assertThrows(NoSuchElementException.class, () -> hei.next()); Assertions.assertFalse(hei.hasNext()); Assertions.assertThrows(NoSuchElementException.class, () -> hei.next()); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/HttpRequestWrapperTest.java0100664 0000000 0000000 00000021275 14403631147 027603 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.net.URI; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.net.URIAuthority; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class HttpRequestWrapperTest { @Test public void testRequestBasics() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, "/stuff"); final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request); Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod()); Assertions.assertEquals("/stuff", httpRequestWrapper.getPath()); Assertions.assertNull(httpRequestWrapper.getAuthority()); Assertions.assertEquals(new URI("/stuff"), httpRequestWrapper.getUri()); httpRequestWrapper.setPath("/another-stuff"); Assertions.assertEquals("/another-stuff", httpRequestWrapper.getPath()); } @Test public void testDefaultRequestConstructors() { final HttpRequest request1 = new BasicHttpRequest("WHATEVER", "/"); final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request1); Assertions.assertEquals("WHATEVER", httpRequestWrapper.getMethod()); Assertions.assertEquals("/", httpRequestWrapper.getPath()); final HttpRequest request2 = new BasicHttpRequest(Method.GET, "/"); final HttpRequestWrapper httpRequestWrapper2 = new HttpRequestWrapper(request2); Assertions.assertEquals(Method.GET.name(), httpRequestWrapper2.getMethod()); Assertions.assertEquals("/", httpRequestWrapper2.getPath()); Assertions.assertThrows(NullPointerException.class, () -> new BasicHttpRequest(Method.GET, (URI) null)); } @Test public void testRequestWithRelativeURI() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("/stuff")); final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request); Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod()); Assertions.assertEquals("/stuff", httpRequestWrapper.getPath()); Assertions.assertNull(httpRequestWrapper.getAuthority()); Assertions.assertEquals(new URI("/stuff"), httpRequestWrapper.getUri()); } @Test public void testRequestWithAbsoluteURI() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("https://host:9443/stuff?param=value")); final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request); Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod()); Assertions.assertEquals("/stuff?param=value", httpRequestWrapper.getPath()); Assertions.assertEquals(new URIAuthority("host", 9443), httpRequestWrapper.getAuthority()); Assertions.assertEquals("https", httpRequestWrapper.getScheme()); Assertions.assertEquals(new URI("https://host:9443/stuff?param=value"), httpRequestWrapper.getUri()); httpRequestWrapper.setScheme((URIScheme.HTTP.id)); Assertions.assertEquals("http", httpRequestWrapper.getScheme()); } @Test public void testRequestWithAbsoluteURIAsPath() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, "https://host:9443/stuff?param=value"); final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request); Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod()); Assertions.assertEquals("/stuff?param=value", httpRequestWrapper.getPath()); Assertions.assertEquals(new URIAuthority("host", 9443), httpRequestWrapper.getAuthority()); Assertions.assertEquals("https", httpRequestWrapper.getScheme()); Assertions.assertEquals(new URI("https://host:9443/stuff?param=value"), httpRequestWrapper.getUri()); } @Test public void testRequestWithNoPath() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("http://host")); final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request); Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod()); Assertions.assertEquals("/", httpRequestWrapper.getPath()); Assertions.assertEquals(new URIAuthority("host"), httpRequestWrapper.getAuthority()); Assertions.assertEquals("http", httpRequestWrapper.getScheme()); Assertions.assertEquals(new URI("http://host/"), httpRequestWrapper.getUri()); } @Test public void testRequestWithUserInfo() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("https://user:pwd@host:9443/stuff?param=value")); final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request); Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod()); Assertions.assertEquals("/stuff?param=value", httpRequestWrapper.getPath()); Assertions.assertEquals(new URIAuthority("user:pwd", "host", 9443), httpRequestWrapper.getAuthority()); Assertions.assertEquals("https", httpRequestWrapper.getScheme()); Assertions.assertEquals(new URI("https://host:9443/stuff?param=value"), httpRequestWrapper.getUri()); } @Test public void testRequestWithAuthority() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new HttpHost("http", "somehost", -1), "/stuff"); final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request); Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod()); Assertions.assertEquals("/stuff", httpRequestWrapper.getPath()); Assertions.assertEquals(new URIAuthority("somehost"), httpRequestWrapper.getAuthority()); Assertions.assertEquals(new URI("http://somehost/stuff"), httpRequestWrapper.getUri()); httpRequestWrapper.setAuthority(new URIAuthority("newHost")); Assertions.assertEquals(new URIAuthority("newHost"), httpRequestWrapper.getAuthority()); } @Test public void testRequestWithAuthorityRelativePath() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new HttpHost("http", "somehost", -1), "stuff"); final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request); Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod()); Assertions.assertEquals("stuff", httpRequestWrapper.getPath()); Assertions.assertEquals("stuff", httpRequestWrapper.getRequestUri()); Assertions.assertEquals(new URIAuthority("somehost"), httpRequestWrapper.getAuthority()); Assertions.assertEquals(new URI("http://somehost/stuff"), httpRequestWrapper.getUri()); } @Test public void testRequestHostWithReservedChars() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, URI.create("http://someuser%21@%21example%21.com/stuff")); final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request); Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod()); Assertions.assertEquals("/stuff", httpRequestWrapper.getPath()); Assertions.assertEquals(new URIAuthority("someuser%21", "%21example%21.com", -1), httpRequestWrapper.getAuthority()); Assertions.assertEquals(new URI("http://%21example%21.com/stuff"), httpRequestWrapper.getUri()); } }httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicMessages.java0100664 0000000 0000000 00000025077 14435411677 026500 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import java.net.URI; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.net.URIBuilder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link org.apache.hc.core5.http.HttpMessage}. * */ public class TestBasicMessages { @Test public void testDefaultResponseConstructors() { final HttpResponse response1 = new BasicHttpResponse(HttpStatus.SC_BAD_REQUEST, "Bad Request"); Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST, response1.getCode()); final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "whatever"); Assertions.assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, response2.getCode()); Assertions.assertEquals("whatever", response2.getReasonPhrase()); } @Test public void testSetResponseStatus() { final HttpResponse response1 = new BasicHttpResponse(200, "OK"); Assertions.assertNotNull(response1.getCode()); Assertions.assertEquals(200, response1.getCode()); final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_BAD_REQUEST, "Bad Request"); Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST, response2.getCode()); final HttpResponse response3 = new BasicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "whatever"); Assertions.assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, response3.getCode()); Assertions.assertEquals("whatever", response3.getReasonPhrase()); final HttpResponse response4 = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); Assertions.assertThrows(IllegalArgumentException.class, () -> response4.setCode(-23)); } @Test public void testDefaultRequestConstructors() { final HttpRequest request1 = new BasicHttpRequest("WHATEVER", "/"); Assertions.assertEquals("WHATEVER", request1.getMethod()); Assertions.assertEquals("/", request1.getPath()); final HttpRequest request2 = new BasicHttpRequest(Method.GET, "/"); Assertions.assertEquals(Method.GET.name(), request2.getMethod()); Assertions.assertEquals("/", request2.getPath()); Assertions.assertThrows(NullPointerException.class, () -> new BasicHttpRequest(Method.GET, (URI) null)); } @Test public void testResponseBasics() { final BasicHttpResponse response = new BasicHttpResponse(200, "OK"); Assertions.assertEquals(200, response.getCode()); Assertions.assertEquals("OK", response.getReasonPhrase()); } @Test public void testResponseStatusLineMutation() { final BasicHttpResponse response = new BasicHttpResponse(200, "OK"); Assertions.assertEquals(200, response.getCode()); Assertions.assertEquals("OK", response.getReasonPhrase()); response.setReasonPhrase("Kind of OK"); Assertions.assertEquals(200, response.getCode()); Assertions.assertEquals("Kind of OK", response.getReasonPhrase()); response.setCode(299); Assertions.assertEquals(299, response.getCode()); Assertions.assertNull(response.getReasonPhrase()); } @Test public void testResponseInvalidStatusCode() { Assertions.assertThrows(IllegalArgumentException.class, () -> new BasicHttpResponse(-200, "OK")); final BasicHttpResponse response = new BasicHttpResponse(200, "OK"); Assertions.assertThrows(IllegalArgumentException.class, () -> response.setCode(-1)); } @Test public void testRequestBasics() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, "/stuff"); Assertions.assertEquals(Method.GET.name(), request.getMethod()); Assertions.assertEquals("/stuff", request.getPath()); Assertions.assertNull(request.getAuthority()); Assertions.assertEquals(new URI("/stuff"), request.getUri()); } @Test public void testRequestWithRelativeURI() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("/stuff")); Assertions.assertEquals(Method.GET.name(), request.getMethod()); Assertions.assertEquals("/stuff", request.getPath()); Assertions.assertNull(request.getAuthority()); Assertions.assertEquals(new URI("/stuff"), request.getUri()); } @Test public void testRequestWithAbsoluteURI() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("https://host:9443/stuff?param=value")); Assertions.assertEquals(Method.GET.name(), request.getMethod()); Assertions.assertEquals("/stuff?param=value", request.getPath()); Assertions.assertEquals(new URIAuthority("host", 9443), request.getAuthority()); Assertions.assertEquals("https", request.getScheme()); Assertions.assertEquals(new URI("https://host:9443/stuff?param=value"), request.getUri()); } @Test public void testRequestWithAbsoluteURIAsPath() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, "https://host:9443/stuff?param=value"); Assertions.assertEquals(Method.GET.name(), request.getMethod()); Assertions.assertEquals("/stuff?param=value", request.getPath()); Assertions.assertEquals(new URIAuthority("host", 9443), request.getAuthority()); Assertions.assertEquals("https", request.getScheme()); Assertions.assertEquals(new URI("https://host:9443/stuff?param=value"), request.getUri()); } @Test public void testRequestWithNoPath() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("http://host")); Assertions.assertEquals(Method.GET.name(), request.getMethod()); Assertions.assertEquals("/", request.getPath()); Assertions.assertEquals(new URIAuthority("host"), request.getAuthority()); Assertions.assertEquals("http", request.getScheme()); Assertions.assertEquals(new URI("http://host/"), request.getUri()); } @Test public void testRequestWithUserInfo() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("https://user:pwd@host:9443/stuff?param=value")); Assertions.assertEquals(Method.GET.name(), request.getMethod()); Assertions.assertEquals("/stuff?param=value", request.getPath()); Assertions.assertEquals(new URIAuthority("user:pwd", "host", 9443), request.getAuthority()); Assertions.assertEquals("https", request.getScheme()); Assertions.assertEquals(new URI("https://host:9443/stuff?param=value"), request.getUri()); } @Test public void testRequestWithAuthority() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new HttpHost("http", "somehost", -1), "/stuff"); Assertions.assertEquals(Method.GET.name(), request.getMethod()); Assertions.assertEquals("/stuff", request.getPath()); Assertions.assertEquals(new URIAuthority("somehost"), request.getAuthority()); Assertions.assertEquals(new URI("http://somehost/stuff"), request.getUri()); } @Test public void testRequestWithAuthorityRelativePath() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, new HttpHost("http", "somehost", -1), "stuff"); Assertions.assertEquals(Method.GET.name(), request.getMethod()); Assertions.assertEquals("stuff", request.getPath()); Assertions.assertEquals(new URIAuthority("somehost"), request.getAuthority()); Assertions.assertEquals(new URI("http://somehost/stuff"), request.getUri()); } @Test public void testRequestHostWithReservedChars() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, URI.create("http://someuser%21@%21example%21.com/stuff")); Assertions.assertEquals(Method.GET.name(), request.getMethod()); Assertions.assertEquals("/stuff", request.getPath()); Assertions.assertEquals(new URIAuthority("someuser%21", "%21example%21.com", -1), request.getAuthority()); Assertions.assertEquals(new URI("http://%21example%21.com/stuff"), request.getUri()); } @Test public void testRequestPathWithMultipleLeadingSlashes() throws Exception { Assertions.assertThrows(IllegalArgumentException.class, () -> new BasicHttpRequest(Method.GET, URI.create("http://host//stuff"))); } @Test public void testRequestAbsoluteRequestUri() throws Exception { final BasicHttpRequest request = new BasicHttpRequest(Method.GET, new HttpHost("http", "somehost", -1), "stuff"); Assertions.assertEquals("stuff", request.getRequestUri()); request.setAbsoluteRequestUri(true); Assertions.assertEquals("http://somehost/stuff", request.getRequestUri()); } @Test public void testModifyingExistingRequest() throws Exception { final URI uri = URI.create("https://example.org"); final HttpRequest request = new BasicHttpRequest(Method.GET, uri); final URI newUri = new URIBuilder(request.getUri()) .addParameter("name", "value") .build(); request.setUri(newUri); Assertions.assertEquals(newUri, request.getUri()); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicHeader.java0100664 0000000 0000000 00000004101 14403631147 026071 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.Header; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests for {@link BasicHeader}. */ public class TestBasicHeader { @Test public void testConstructor() { final Header param = new BasicHeader("name", "value"); Assertions.assertEquals("name", param.getName()); Assertions.assertEquals("value", param.getValue()); } @Test public void testInvalidName() { Assertions.assertThrows(NullPointerException.class, () -> new BasicHeader(null, null)); } @Test public void testToString() { final Header param1 = new BasicHeader("name1", "value1"); Assertions.assertEquals("name1: value1", param1.toString()); final Header param2 = new BasicHeader("name1", null); Assertions.assertEquals("name1: ", param2.toString()); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestNameValuePair.java0100664 0000000 0000000 00000016331 14403631147 026440 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.NameValuePair; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link NameValuePair}. * */ public class TestNameValuePair { @Test public void testConstructor() { final NameValuePair param = new BasicNameValuePair("name", "value"); Assertions.assertEquals("name", param.getName()); Assertions.assertEquals("value", param.getValue()); } @Test public void testInvalidName() { Assertions.assertThrows(NullPointerException.class, () -> new BasicNameValuePair(null, null)); } @Test public void testToString() { final NameValuePair param1 = new BasicNameValuePair("name1", "value1"); Assertions.assertEquals("name1=value1", param1.toString()); final NameValuePair param2 = new BasicNameValuePair("name1", null); Assertions.assertEquals("name1", param2.toString()); } @Test public void testNullNotEqual() throws Exception { final NameValuePair NameValuePair = new BasicNameValuePair("name", "value"); Assertions.assertNotEquals(null, NameValuePair); } @Test public void testObjectNotEqual() throws Exception { final NameValuePair NameValuePair = new BasicNameValuePair("name", "value"); Assertions.assertNotEquals(NameValuePair, new Object()); } @Test public void testNameEquals() throws Exception { final NameValuePair NameValuePair = new BasicNameValuePair("name", "value"); final NameValuePair NameValuePair2 = new BasicNameValuePair("NAME", "value"); Assertions.assertEquals(NameValuePair, NameValuePair2); Assertions.assertEquals(NameValuePair.hashCode(), NameValuePair2.hashCode()); final NameValuePair NameValuePair3 = new BasicNameValuePair("NAME", "value"); final NameValuePair NameValuePair4 = new BasicNameValuePair("name", "value"); Assertions.assertEquals(NameValuePair3, NameValuePair4); Assertions.assertEquals(NameValuePair3.hashCode(), NameValuePair4.hashCode()); } @Test public void testValueEquals() throws Exception { final NameValuePair NameValuePair = new BasicNameValuePair("name", "value"); final NameValuePair NameValuePair2 = new BasicNameValuePair("name", "value"); Assertions.assertEquals(NameValuePair, NameValuePair2); Assertions.assertEquals(NameValuePair.hashCode(), NameValuePair2.hashCode()); } @Test public void testNameNotEqual() throws Exception { final NameValuePair NameValuePair = new BasicNameValuePair("name", "value"); final NameValuePair NameValuePair2 = new BasicNameValuePair("name2", "value"); Assertions.assertNotEquals(NameValuePair, NameValuePair2); } @Test public void testValueNotEqual() throws Exception { final NameValuePair NameValuePair = new BasicNameValuePair("name", "value"); final NameValuePair NameValuePair2 = new BasicNameValuePair("name", "value2"); Assertions.assertNotEquals(NameValuePair, NameValuePair2); final NameValuePair NameValuePair3 = new BasicNameValuePair("name", "value"); final NameValuePair NameValuePair4 = new BasicNameValuePair("name", "VALUE"); Assertions.assertNotEquals(NameValuePair3, NameValuePair4); final NameValuePair NameValuePair5 = new BasicNameValuePair("name", "VALUE"); final NameValuePair NameValuePair6 = new BasicNameValuePair("name", "value"); Assertions.assertNotEquals(NameValuePair5, NameValuePair6); } @Test public void testNullValuesEquals() throws Exception { final NameValuePair NameValuePair = new BasicNameValuePair("name", null); final NameValuePair NameValuePair2 = new BasicNameValuePair("name", null); Assertions.assertEquals(NameValuePair, NameValuePair2); Assertions.assertEquals(NameValuePair.hashCode(), NameValuePair2.hashCode()); } @Test public void testNullValueNotEqual() throws Exception { final NameValuePair NameValuePair = new BasicNameValuePair("name", null); final NameValuePair NameValuePair2 = new BasicNameValuePair("name", "value"); Assertions.assertNotEquals(NameValuePair, NameValuePair2); } @Test public void testNullValue2NotEqual() throws Exception { final NameValuePair NameValuePair = new BasicNameValuePair("name", "value"); final NameValuePair NameValuePair2 = new BasicNameValuePair("name", null); Assertions.assertNotEquals(NameValuePair, NameValuePair2); } @Test public void testEquals() throws Exception { final NameValuePair NameValuePair = new BasicNameValuePair("name", "value"); final NameValuePair NameValuePair2 = new BasicNameValuePair("name", "value"); Assertions.assertEquals(NameValuePair, NameValuePair); Assertions.assertEquals(NameValuePair.hashCode(), NameValuePair.hashCode()); Assertions.assertEquals(NameValuePair2, NameValuePair2); Assertions.assertEquals(NameValuePair2.hashCode(), NameValuePair2.hashCode()); Assertions.assertEquals(NameValuePair, NameValuePair2); Assertions.assertEquals(NameValuePair.hashCode(), NameValuePair2.hashCode()); } @Test public void testHashCode() throws Exception { final NameValuePair NameValuePair = new BasicNameValuePair("name", null); final NameValuePair NameValuePair2 = new BasicNameValuePair("name2", null); Assertions.assertNotEquals(NameValuePair.hashCode(), NameValuePair2.hashCode()); final NameValuePair NameValuePair3 = new BasicNameValuePair("name", "value"); final NameValuePair NameValuePair4 = new BasicNameValuePair("name", "value2"); Assertions.assertNotEquals(NameValuePair3.hashCode(), NameValuePair4.hashCode()); final NameValuePair NameValuePair5 = new BasicNameValuePair("name", "value"); final NameValuePair NameValuePair6 = new BasicNameValuePair("name", null); Assertions.assertNotEquals(NameValuePair5.hashCode(), NameValuePair6.hashCode()); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicHeaderValueParser.java0100664 0000000 0000000 00000032253 14403631147 030254 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests for header value parsing. * * @version $Id$ */ public class TestBasicHeaderValueParser { private BasicHeaderValueParser parser; @BeforeEach public void setup() { this.parser = BasicHeaderValueParser.INSTANCE; } @Test public void testParseHeaderElements() throws Exception { final String headerValue = "name1 = value1; name2; name3=\"value3\" , name4=value4; " + "name5=value5, name6= ; name7 = value7; name8 = \" value8\""; final CharArrayBuffer buf = new CharArrayBuffer(64); buf.append(headerValue); final ParserCursor cursor = new ParserCursor(0, buf.length()); final HeaderElement[] elements = this.parser.parseElements(buf, cursor); // there are 3 elements Assertions.assertEquals(3,elements.length); // 1st element Assertions.assertEquals("name1",elements[0].getName()); Assertions.assertEquals("value1",elements[0].getValue()); // 1st element has 2 getParameters() Assertions.assertEquals(2,elements[0].getParameters().length); Assertions.assertEquals("name2",elements[0].getParameters()[0].getName()); Assertions.assertNull(elements[0].getParameters()[0].getValue()); Assertions.assertEquals("name3",elements[0].getParameters()[1].getName()); Assertions.assertEquals("value3",elements[0].getParameters()[1].getValue()); // 2nd element Assertions.assertEquals("name4",elements[1].getName()); Assertions.assertEquals("value4",elements[1].getValue()); // 2nd element has 1 parameter Assertions.assertEquals(1,elements[1].getParameters().length); Assertions.assertEquals("name5",elements[1].getParameters()[0].getName()); Assertions.assertEquals("value5",elements[1].getParameters()[0].getValue()); // 3rd element Assertions.assertEquals("name6",elements[2].getName()); Assertions.assertEquals("",elements[2].getValue()); // 3rd element has 2 getParameters() Assertions.assertEquals(2,elements[2].getParameters().length); Assertions.assertEquals("name7",elements[2].getParameters()[0].getName()); Assertions.assertEquals("value7",elements[2].getParameters()[0].getValue()); Assertions.assertEquals("name8",elements[2].getParameters()[1].getName()); Assertions.assertEquals(" value8",elements[2].getParameters()[1].getValue()); } @Test public void testParseHEEscaped() { final String headerValue = "test1 = \"\\\"stuff\\\"\", test2= \"\\\\\", test3 = \"stuff, stuff\""; final CharArrayBuffer buf = new CharArrayBuffer(64); buf.append(headerValue); final ParserCursor cursor = new ParserCursor(0, buf.length()); final HeaderElement[] elements = this.parser.parseElements(buf, cursor); Assertions.assertEquals(3, elements.length); Assertions.assertEquals("test1", elements[0].getName()); Assertions.assertEquals("\"stuff\"", elements[0].getValue()); Assertions.assertEquals("test2", elements[1].getName()); Assertions.assertEquals("\\", elements[1].getValue()); Assertions.assertEquals("test3", elements[2].getName()); Assertions.assertEquals("stuff, stuff", elements[2].getValue()); } @Test public void testHEFringeCase1() throws Exception { final String headerValue = "name1 = value1,"; final CharArrayBuffer buf = new CharArrayBuffer(64); buf.append(headerValue); final ParserCursor cursor = new ParserCursor(0, buf.length()); final HeaderElement[] elements = this.parser.parseElements(buf, cursor); Assertions.assertEquals(1, elements.length, "Number of elements"); } @Test public void testHEFringeCase2() throws Exception { final String headerValue = "name1 = value1, "; final CharArrayBuffer buf = new CharArrayBuffer(64); buf.append(headerValue); final ParserCursor cursor = new ParserCursor(0, buf.length()); final HeaderElement[] elements = this.parser.parseElements(buf, cursor); Assertions.assertEquals(1, elements.length, "Number of elements"); } @Test public void testHEFringeCase3() throws Exception { final String headerValue = ",, ,, ,"; final CharArrayBuffer buf = new CharArrayBuffer(64); buf.append(headerValue); final ParserCursor cursor = new ParserCursor(0, buf.length()); final HeaderElement[] elements = this.parser.parseElements(buf, cursor); Assertions.assertEquals(0, elements.length, "Number of elements"); } @Test public void testNVParse() { String s = "test"; CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append(s); ParserCursor cursor = new ParserCursor(0, s.length()); NameValuePair param = this.parser.parseNameValuePair(buffer, cursor); Assertions.assertEquals("test", param.getName()); Assertions.assertNull(param.getValue()); Assertions.assertEquals(s.length(), cursor.getPos()); Assertions.assertTrue(cursor.atEnd()); s = "test;"; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); param = this.parser.parseNameValuePair(buffer, cursor); Assertions.assertEquals("test", param.getName()); Assertions.assertNull(param.getValue()); Assertions.assertEquals(s.length(), cursor.getPos()); Assertions.assertTrue(cursor.atEnd()); s = "test ,12"; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); param = this.parser.parseNameValuePair(buffer, cursor); Assertions.assertEquals("test", param.getName()); Assertions.assertNull(param.getValue()); Assertions.assertEquals(s.length() - 2, cursor.getPos()); Assertions.assertFalse(cursor.atEnd()); s = "test=stuff"; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); param = this.parser.parseNameValuePair(buffer, cursor); Assertions.assertEquals("test", param.getName()); Assertions.assertEquals("stuff", param.getValue()); Assertions.assertEquals(s.length(), cursor.getPos()); Assertions.assertTrue(cursor.atEnd()); s = " test = stuff "; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); param = this.parser.parseNameValuePair(buffer, cursor); Assertions.assertEquals("test", param.getName()); Assertions.assertEquals("stuff", param.getValue()); Assertions.assertEquals(s.length(), cursor.getPos()); Assertions.assertTrue(cursor.atEnd()); s = " test = stuff ;1234"; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); param = this.parser.parseNameValuePair(buffer, cursor); Assertions.assertEquals("test", param.getName()); Assertions.assertEquals("stuff", param.getValue()); Assertions.assertEquals(s.length() - 4, cursor.getPos()); Assertions.assertFalse(cursor.atEnd()); s = "test = \"stuff\""; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); param = this.parser.parseNameValuePair(buffer, cursor); Assertions.assertEquals("test", param.getName()); Assertions.assertEquals("stuff", param.getValue()); s = "test = \" stuff\\\"\""; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); param = this.parser.parseNameValuePair(buffer, cursor); Assertions.assertEquals("test", param.getName()); Assertions.assertEquals(" stuff\"", param.getValue()); s = " test"; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); param = this.parser.parseNameValuePair(buffer, cursor); Assertions.assertEquals("test", param.getName()); Assertions.assertNull(param.getValue()); s = " "; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); param = this.parser.parseNameValuePair(buffer, cursor); Assertions.assertEquals("", param.getName()); Assertions.assertNull(param.getValue()); s = " = stuff "; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); param = this.parser.parseNameValuePair(buffer, cursor); Assertions.assertEquals("", param.getName()); Assertions.assertEquals("stuff", param.getValue()); } @Test public void testNVParseAll() { String s = "test; test1 = stuff ; test2 = \"stuff; stuff\"; test3=\"stuff"; CharArrayBuffer buffer = new CharArrayBuffer(16); buffer.append(s); ParserCursor cursor = new ParserCursor(0, s.length()); NameValuePair[] params = this.parser.parseParameters(buffer, cursor); Assertions.assertEquals("test", params[0].getName()); Assertions.assertNull(params[0].getValue()); Assertions.assertEquals("test1", params[1].getName()); Assertions.assertEquals("stuff", params[1].getValue()); Assertions.assertEquals("test2", params[2].getName()); Assertions.assertEquals("stuff; stuff", params[2].getValue()); Assertions.assertEquals("test3", params[3].getName()); Assertions.assertEquals("stuff", params[3].getValue()); Assertions.assertEquals(s.length(), cursor.getPos()); Assertions.assertTrue(cursor.atEnd()); s = "test; test1 = stuff ; test2 = \"stuff; stuff\"; test3=\"stuff\",123"; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); params = this.parser.parseParameters(buffer, cursor); Assertions.assertEquals("test", params[0].getName()); Assertions.assertNull(params[0].getValue()); Assertions.assertEquals("test1", params[1].getName()); Assertions.assertEquals("stuff", params[1].getValue()); Assertions.assertEquals("test2", params[2].getName()); Assertions.assertEquals("stuff; stuff", params[2].getValue()); Assertions.assertEquals("test3", params[3].getName()); Assertions.assertEquals("stuff", params[3].getValue()); Assertions.assertEquals(s.length() - 3, cursor.getPos()); Assertions.assertFalse(cursor.atEnd()); s = " "; buffer = new CharArrayBuffer(16); buffer.append(s); cursor = new ParserCursor(0, s.length()); params = this.parser.parseParameters(buffer, cursor); Assertions.assertEquals(0, params.length); } @Test public void testNVParseEscaped() { final String headerValue = "test1 = \"\\\"stuff\\\"\"; test2= \"\\\\\"; test3 = \"stuff; stuff\""; final CharArrayBuffer buf = new CharArrayBuffer(64); buf.append(headerValue); final ParserCursor cursor = new ParserCursor(0, buf.length()); final NameValuePair[] params = this.parser.parseParameters(buf, cursor); Assertions.assertEquals(3, params.length); Assertions.assertEquals("test1", params[0].getName()); Assertions.assertEquals("\"stuff\"", params[0].getValue()); Assertions.assertEquals("test2", params[1].getName()); Assertions.assertEquals("\\", params[1].getValue()); Assertions.assertEquals("test3", params[2].getName()); Assertions.assertEquals("stuff; stuff", params[2].getValue()); } } httpcore5/src/test/java/org/apache/hc/core5/http/message/TestBasicLineParser.java0100664 0000000 0000000 00000033152 14403631147 026755 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.message; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests for {@link BasicLineParser}. * */ public class TestBasicLineParser { private BasicLineParser parser; @BeforeEach public void setup() { this.parser = BasicLineParser.INSTANCE; } @Test public void testRLParse() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); //typical request line buf.clear(); buf.append("GET /stuff HTTP/1.1"); RequestLine requestline = this.parser.parseRequestLine(buf); Assertions.assertEquals("GET /stuff HTTP/1.1", requestline.toString()); Assertions.assertEquals(Method.GET.name(), requestline.getMethod()); Assertions.assertEquals("/stuff", requestline.getUri()); Assertions.assertEquals(HttpVersion.HTTP_1_1, requestline.getProtocolVersion()); //Lots of blanks buf.clear(); buf.append(" GET /stuff HTTP/1.1 "); requestline = this.parser.parseRequestLine(buf); Assertions.assertEquals("GET /stuff HTTP/1.1", requestline.toString()); Assertions.assertEquals(Method.GET.name(), requestline.getMethod()); Assertions.assertEquals("/stuff", requestline.getUri()); Assertions.assertEquals(HttpVersion.HTTP_1_1, requestline.getProtocolVersion()); //this is not strictly valid, but is lenient buf.clear(); buf.append("\rGET /stuff HTTP/1.1"); requestline = this.parser.parseRequestLine(buf); Assertions.assertEquals(Method.GET.name(), requestline.getMethod()); Assertions.assertEquals("/stuff", requestline.getUri()); Assertions.assertEquals(HttpVersion.HTTP_1_1, requestline.getProtocolVersion()); } @Test public void testRLParseFailure() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); buf.clear(); buf.append(" "); Assertions.assertThrows(ParseException.class, () -> parser.parseRequestLine(buf)); buf.clear(); buf.append(" GET"); Assertions.assertThrows(ParseException.class, () -> parser.parseRequestLine(buf)); buf.clear(); buf.append("GET /stuff"); Assertions.assertThrows(ParseException.class, () -> parser.parseRequestLine(buf)); buf.clear(); buf.append("GET/stuff HTTP/1.1"); Assertions.assertThrows(ParseException.class, () -> parser.parseRequestLine(buf)); buf.clear(); buf.append("GET /stuff HTTP/1.1 Oooooooooooppsie"); Assertions.assertThrows(ParseException.class, () -> parser.parseRequestLine(buf)); } @Test public void testSLParse() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); //typical status line buf.clear(); buf.append("HTTP/1.1 200 OK"); StatusLine statusLine = this.parser.parseStatusLine(buf); Assertions.assertEquals("HTTP/1.1 200 OK", statusLine.toString()); Assertions.assertEquals(HttpVersion.HTTP_1_1, statusLine.getProtocolVersion()); Assertions.assertEquals(200, statusLine.getStatusCode()); Assertions.assertEquals("OK", statusLine.getReasonPhrase()); //status line with multi word reason phrase buf.clear(); buf.append("HTTP/1.1 404 Not Found"); statusLine = this.parser.parseStatusLine(buf); Assertions.assertEquals(404, statusLine.getStatusCode()); Assertions.assertEquals("Not Found", statusLine.getReasonPhrase()); //reason phrase can be anyting buf.clear(); buf.append("HTTP/1.1 404 Non Trouve"); statusLine = this.parser.parseStatusLine(buf); Assertions.assertEquals("Non Trouve", statusLine.getReasonPhrase()); //its ok to end with a \n\r buf.clear(); buf.append("HTTP/1.1 404 Not Found\r\n"); statusLine = this.parser.parseStatusLine(buf); Assertions.assertEquals("Not Found", statusLine.getReasonPhrase()); //this is valid according to the Status-Line BNF buf.clear(); buf.append("HTTP/1.1 200 "); statusLine = this.parser.parseStatusLine(buf); Assertions.assertEquals(200, statusLine.getStatusCode()); Assertions.assertEquals("", statusLine.getReasonPhrase()); //this is not strictly valid, but is lenient buf.clear(); buf.append("HTTP/1.1 200"); statusLine = this.parser.parseStatusLine(buf); Assertions.assertEquals(200, statusLine.getStatusCode()); Assertions.assertEquals("", statusLine.getReasonPhrase()); //this is not strictly valid, but is lenient buf.clear(); buf.append("HTTP/1.1 200 OK"); statusLine = this.parser.parseStatusLine(buf); Assertions.assertEquals(200, statusLine.getStatusCode()); Assertions.assertEquals("OK", statusLine.getReasonPhrase()); //this is not strictly valid, but is lenient buf.clear(); buf.append("\nHTTP/1.1 200 OK"); statusLine = this.parser.parseStatusLine(buf); Assertions.assertEquals(200, statusLine.getStatusCode()); Assertions.assertEquals("OK", statusLine.getReasonPhrase()); Assertions.assertEquals(HttpVersion.HTTP_1_1, statusLine.getProtocolVersion()); //this is not strictly valid, but is lenient buf.clear(); buf.append(" HTTP/1.1 200 OK"); statusLine = this.parser.parseStatusLine(buf); Assertions.assertEquals(200, statusLine.getStatusCode()); Assertions.assertEquals("OK", statusLine.getReasonPhrase()); Assertions.assertEquals(HttpVersion.HTTP_1_1, statusLine.getProtocolVersion()); } @Test public void testSLParseFailure() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); buf.clear(); buf.append("xxx 200 OK"); Assertions.assertThrows(ParseException.class, () -> parser.parseStatusLine(buf)); buf.clear(); buf.append("HTTP/1.1 xxx OK"); Assertions.assertThrows(ParseException.class, () -> parser.parseStatusLine(buf)); buf.clear(); buf.append("HTTP/1.1 "); Assertions.assertThrows(ParseException.class, () -> parser.parseStatusLine(buf)); buf.clear(); buf.append("HTTP/1.1"); Assertions.assertThrows(ParseException.class, () -> parser.parseStatusLine(buf)); buf.clear(); buf.append("HTTP/1.1 -200 OK"); Assertions.assertThrows(ParseException.class, () -> parser.parseStatusLine(buf)); } @Test public void testHttpVersionParsing() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(16); buffer.append("HTTP/1.1"); ParserCursor cursor = new ParserCursor(0, buffer.length()); HttpVersion version = (HttpVersion) parser.parseProtocolVersion(buffer, cursor); Assertions.assertEquals("HTTP", version.getProtocol(), "HTTP protocol name"); Assertions.assertEquals(1, version.getMajor(), "HTTP major version number"); Assertions.assertEquals(1, version.getMinor(), "HTTP minor version number"); Assertions.assertEquals("HTTP/1.1", version.toString(), "HTTP version number"); Assertions.assertEquals(buffer.length(), cursor.getPos()); Assertions.assertTrue(cursor.atEnd()); buffer.clear(); buffer.append("HTTP/1.123 123"); cursor = new ParserCursor(0, buffer.length()); version = (HttpVersion) parser.parseProtocolVersion(buffer, cursor); Assertions.assertEquals( "HTTP", version.getProtocol(), "HTTP protocol name"); Assertions.assertEquals( 1, version.getMajor(), "HTTP major version number"); Assertions.assertEquals(123, version.getMinor(), "HTTP minor version number"); Assertions.assertEquals("HTTP/1.123", version.toString(), "HTTP version number"); Assertions.assertEquals(' ', buffer.charAt(cursor.getPos())); Assertions.assertEquals(buffer.length() - 4, cursor.getPos()); Assertions.assertFalse(cursor.atEnd()); } @Test public void testInvalidHttpVersionParsing() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(16); buffer.clear(); buffer.append(" "); final ParserCursor cursor1 = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parseProtocolVersion(buffer, cursor1)); buffer.clear(); buffer.append("HTT"); final ParserCursor cursor2 = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parseProtocolVersion(buffer, cursor2)); buffer.clear(); buffer.append("crap"); final ParserCursor cursor3 = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parseProtocolVersion(buffer, cursor3)); buffer.clear(); buffer.append("HTTP/crap"); final ParserCursor cursor4 = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parseProtocolVersion(buffer, cursor4)); buffer.clear(); buffer.append("HTTP/1"); final ParserCursor cursor5 = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parseProtocolVersion(buffer, cursor5)); buffer.clear(); buffer.append("HTTP/1234"); final ParserCursor cursor6 = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parseProtocolVersion(buffer, cursor6)); buffer.clear(); buffer.append("HTTP/1."); final ParserCursor cursor7 = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parseProtocolVersion(buffer, cursor7)); buffer.clear(); buffer.append("HTTP/whatever.whatever whatever"); final ParserCursor cursor8 = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parseProtocolVersion(buffer, cursor8)); buffer.clear(); buffer.append("HTTP/1.whatever whatever"); final ParserCursor cursor9 = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parseProtocolVersion(buffer, cursor9)); } @Test public void testHeaderParse() throws Exception { final CharArrayBuffer buf = new CharArrayBuffer(64); //typical request line buf.clear(); buf.append("header: blah"); Header header = this.parser.parseHeader(buf); Assertions.assertEquals("header", header.getName()); Assertions.assertEquals("blah", header.getValue()); //Lots of blanks buf.clear(); buf.append(" header: blah "); header = this.parser.parseHeader(buf); Assertions.assertEquals("header", header.getName()); Assertions.assertEquals("blah", header.getValue()); } @Test public void testInvalidHeaderParsing() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(16); buffer.clear(); buffer.append(""); Assertions.assertThrows(ParseException.class, () -> parser.parseHeader(buffer)); buffer.clear(); buffer.append("blah"); Assertions.assertThrows(ParseException.class, () -> parser.parseHeader(buffer)); buffer.clear(); buffer.append(":"); Assertions.assertThrows(ParseException.class, () -> parser.parseHeader(buffer)); buffer.clear(); buffer.append(" :"); Assertions.assertThrows(ParseException.class, () -> parser.parseHeader(buffer)); buffer.clear(); buffer.append(": blah"); Assertions.assertThrows(ParseException.class, () -> parser.parseHeader(buffer)); buffer.clear(); buffer.append(" : blah"); Assertions.assertThrows(ParseException.class, () -> parser.parseHeader(buffer)); buffer.clear(); buffer.append("header : blah"); Assertions.assertThrows(ParseException.class, () -> parser.parseHeader(buffer)); } } httpcore5/src/test/java/org/apache/hc/core5/http/protocol/0040775 0000000 0000000 00000000000 14435411677 022471 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriPatternType.java0100664 0000000 0000000 00000003746 14403631147 027131 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestUriPatternType { @Test public void testRegex() { final LookupRegistry matcher = UriPatternType.newMatcher(UriPatternType.REGEX); Assertions.assertTrue(matcher instanceof UriRegexMatcher); } @Test public void testUriPattern() { final LookupRegistry matcher = UriPatternType.newMatcher(UriPatternType.URI_PATTERN); Assertions.assertTrue(matcher instanceof UriPatternMatcher); } @Test public void testUriPatternInOrder() { final LookupRegistry matcher = UriPatternType.newMatcher(UriPatternType.URI_PATTERN_IN_ORDER); Assertions.assertTrue(matcher instanceof UriPatternOrderedMatcher); } } httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestRequestHandlerRegistry.java0100664 0000000 0000000 00000010240 14403631147 030634 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.MisdirectedRequestException; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestRequestHandlerRegistry { private RequestHandlerRegistry handlerRegistry; private HttpContext context; @BeforeEach public void setUp() { handlerRegistry = new RequestHandlerRegistry<>("myhost", UriPatternMatcher::new); context = new BasicHttpContext(); } @Test public void testResolveByRequestUri() throws Exception { handlerRegistry.register(null, "/test*", "stuff"); Assertions.assertNotEquals("stuff", handlerRegistry.resolve(new BasicHttpRequest(Method.GET, "/"), context)); Assertions.assertNotEquals("stuff", handlerRegistry.resolve(new BasicHttpRequest(Method.GET, "/abc"), context)); Assertions.assertEquals("stuff", handlerRegistry.resolve(new BasicHttpRequest(Method.GET, "/test"), context)); Assertions.assertEquals("stuff", handlerRegistry.resolve(new BasicHttpRequest(Method.GET, "/testabc"), context)); } @Test public void testByRequestUriWithQuery() throws Exception { handlerRegistry.register(null, "/test*", "stuff"); Assertions.assertEquals("stuff", handlerRegistry.resolve(new BasicHttpRequest(Method.GET, "/test?test=a"), context)); Assertions.assertNotEquals("stuff", handlerRegistry.resolve(new BasicHttpRequest(Method.GET, "/tes?test=a"), context)); } @Test public void testResolveByHostnameAndRequestUri() throws Exception { handlerRegistry.register("myhost", "/test*", "stuff"); Assertions.assertEquals("stuff", handlerRegistry.resolve(new BasicHttpRequest(Method.GET, new HttpHost("myhost"), "/test"), context)); Assertions.assertEquals("stuff", handlerRegistry.resolve(new BasicHttpRequest(Method.GET, new HttpHost("MyHost"), "/testabc"), context)); } @Test public void testResolveByLocalhostAndRequestUri() throws Exception { handlerRegistry.register("myhost", "/test*", "stuff"); Assertions.assertEquals("stuff", handlerRegistry.resolve(new BasicHttpRequest(Method.GET, new HttpHost("localhost"), "/test"), context)); Assertions.assertEquals("stuff", handlerRegistry.resolve(new BasicHttpRequest(Method.GET, new HttpHost("LocalHost"), "/testabc"), context)); Assertions.assertEquals("stuff", handlerRegistry.resolve(new BasicHttpRequest(Method.GET, new HttpHost("127.0.0.1"), "/testabc"), context)); } @Test public void testResolveByUnknownHostname() throws Exception { handlerRegistry.register("myhost", "/test*", "stuff"); Assertions.assertThrows(MisdirectedRequestException.class, () -> handlerRegistry.resolve(new BasicHttpRequest(Method.GET, new HttpHost("otherhost"), "/test"), context)); } } httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriRegexMatcher.java0100664 0000000 0000000 00000015445 14403631147 027227 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestUriRegexMatcher { @Test public void testRegisterUnregister() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object h3 = new Object(); final LookupRegistry matcher = new UriRegexMatcher<>(); matcher.register("/h1", h1); matcher.register("/h2", h2); matcher.register("/h3", h3); Object h; h = matcher.lookup("/h1"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); h = matcher.lookup("/h2"); Assertions.assertNotNull(h); Assertions.assertSame(h2, h); h = matcher.lookup("/h3"); Assertions.assertNotNull(h); Assertions.assertSame(h3, h); matcher.unregister("/h1"); h = matcher.lookup("/h1"); Assertions.assertNull(h); } @Test public void testRegisterNull() throws Exception { final LookupRegistry matcher = new UriRegexMatcher<>(); Assertions.assertThrows(NullPointerException.class, () -> matcher.register(null, null)); } @Test public void testWildCardMatching1a() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object h3 = new Object(); final Object def = new Object(); final LookupRegistry matcher = new UriRegexMatcher<>(); matcher.register(".*", def); matcher.register("/one/.*", h1); matcher.register("/one/two/.*", h2); matcher.register("/one/two/three/.*", h3); Object h; h = matcher.lookup("/one/request"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); h = matcher.lookup("/one/two/request"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); h = matcher.lookup("/one/two/three/request"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); h = matcher.lookup("default/request"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); } @Test public void testWildCardMatching1b() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object h3 = new Object(); final Object def = new Object(); final LookupRegistry matcher = new UriRegexMatcher<>(); matcher.register("/one/two/three/.*", h3); matcher.register("/one/two/.*", h2); matcher.register("/one/.*", h1); matcher.register(".*", def); Object h; h = matcher.lookup("/one/request"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); h = matcher.lookup("/one/two/request"); Assertions.assertNotNull(h); Assertions.assertSame(h2, h); h = matcher.lookup("/one/two/three/request"); Assertions.assertNotNull(h); Assertions.assertSame(h3, h); h = matcher.lookup("default/request"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); } @Test public void testWildCardMatching2a() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object def = new Object(); final LookupRegistry matcher = new UriRegexMatcher<>(); matcher.register(".*", def); matcher.register(".*\\.view", h1); matcher.register(".*\\.form", h2); Object h; h = matcher.lookup("/that.view"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); h = matcher.lookup("/that.form"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); h = matcher.lookup("/whatever"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); } @Test public void testWildCardMatching2b() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object def = new Object(); final LookupRegistry matcher = new UriRegexMatcher<>(); matcher.register(".*\\.form", h2); matcher.register(".*\\.view", h1); matcher.register(".*", def); Object h; h = matcher.lookup("/that.view"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); h = matcher.lookup("/that.form"); Assertions.assertNotNull(h); Assertions.assertSame(h2, h); h = matcher.lookup("/whatever"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); } @Test public void testSuffixPatternOverPrefixPatternMatch() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final LookupRegistry matcher = new UriRegexMatcher<>(); matcher.register("/ma.*", h1); matcher.register(".*tch", h2); final Object h = matcher.lookup("/match"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); } @Test public void testRegisterInvalidInput() throws Exception { final LookupRegistry matcher = new UriRegexMatcher<>(); Assertions.assertThrows(NullPointerException.class, () -> matcher.register(null, null)); } @Test public void testLookupInvalidInput() throws Exception { final LookupRegistry matcher = new UriRegexMatcher<>(); Assertions.assertThrows(NullPointerException.class, () -> matcher.lookup(null)); } } httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestHttpExecutionContext.java0100664 0000000 0000000 00000007210 14403631147 030330 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; // the name of this test is historic, the implementation classes of HttpContext // have been renamed to BasicHttpContext and SyncBasicHttpContext public class TestHttpExecutionContext { @Test public void testContextOperations() { final HttpContext parentContext = new BasicHttpContext(null); final HttpContext currentContext = new BasicHttpContext(parentContext); parentContext.setAttribute("param1", "1"); parentContext.setAttribute("param2", "2"); currentContext.setAttribute("param3", "3"); currentContext.setAttribute("param2", "4"); Assertions.assertEquals("1", parentContext.getAttribute("param1")); Assertions.assertEquals("2", parentContext.getAttribute("param2")); Assertions.assertNull(parentContext.getAttribute("param3")); Assertions.assertEquals("1", currentContext.getAttribute("param1")); Assertions.assertEquals("4", currentContext.getAttribute("param2")); Assertions.assertEquals("3", currentContext.getAttribute("param3")); Assertions.assertNull(currentContext.getAttribute("param4")); currentContext.removeAttribute("param1"); currentContext.removeAttribute("param2"); currentContext.removeAttribute("param3"); currentContext.removeAttribute("param4"); Assertions.assertEquals("1", currentContext.getAttribute("param1")); Assertions.assertEquals("2", currentContext.getAttribute("param2")); Assertions.assertNull(currentContext.getAttribute("param3")); Assertions.assertNull(currentContext.getAttribute("param4")); } @Test public void testEmptyContextOperations() { final HttpContext currentContext = new BasicHttpContext(null); Assertions.assertNull(currentContext.getAttribute("param1")); currentContext.removeAttribute("param1"); Assertions.assertNull(currentContext.getAttribute("param1")); } @Test public void testContextInvalidInput() throws Exception { final HttpContext currentContext = new BasicHttpContext(null); Assertions.assertThrows(NullPointerException.class, () -> currentContext.setAttribute(null, null)); Assertions.assertThrows(NullPointerException.class, () -> currentContext.getAttribute(null)); Assertions.assertThrows(NullPointerException.class, () -> currentContext.removeAttribute(null)); } } httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriPatternMatcher.java0100664 0000000 0000000 00000014103 14403631147 027560 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestUriPatternMatcher { @Test public void testEntrySet() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object h3 = new Object(); final UriPatternMatcher matcher = new UriPatternMatcher<>(); Assertions.assertEquals(0, matcher.entrySet().size()); matcher.register("/h1", h1); Assertions.assertEquals(1, matcher.entrySet().size()); matcher.register("/h2", h2); Assertions.assertEquals(2, matcher.entrySet().size()); matcher.register("/h3", h3); Assertions.assertEquals(3, matcher.entrySet().size()); } @Test public void testRegisterUnregister() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object h3 = new Object(); final LookupRegistry matcher = new UriPatternMatcher<>(); matcher.register("/h1", h1); matcher.register("/h2", h2); matcher.register("/h3", h3); Object h; h = matcher.lookup("/h1"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); h = matcher.lookup("/h2"); Assertions.assertNotNull(h); Assertions.assertSame(h2, h); h = matcher.lookup("/h3"); Assertions.assertNotNull(h); Assertions.assertSame(h3, h); matcher.unregister("/h1"); h = matcher.lookup("/h1"); Assertions.assertNull(h); } @Test public void testRegisterNull() throws Exception { final LookupRegistry matcher = new UriPatternMatcher<>(); Assertions.assertThrows(NullPointerException.class, () -> matcher.register(null, null)); } @Test public void testWildCardMatching1() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object h3 = new Object(); final Object def = new Object(); final LookupRegistry matcher = new UriPatternMatcher<>(); matcher.register("*", def); matcher.register("/one/*", h1); matcher.register("/one/two/*", h2); matcher.register("/one/two/three/*", h3); Object h; h = matcher.lookup("/one/request"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); h = matcher.lookup("/one/two/request"); Assertions.assertNotNull(h); Assertions.assertSame(h2, h); h = matcher.lookup("/one/two/three/request"); Assertions.assertNotNull(h); Assertions.assertSame(h3, h); h = matcher.lookup("default/request"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); } @Test public void testWildCardMatching2() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object def = new Object(); final LookupRegistry matcher = new UriPatternMatcher<>(); matcher.register("*", def); matcher.register("*.view", h1); matcher.register("*.form", h2); Object h; h = matcher.lookup("/that.view"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); h = matcher.lookup("/that.form"); Assertions.assertNotNull(h); Assertions.assertSame(h2, h); h = matcher.lookup("/whatever"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); } @Test public void testSuffixPatternOverPrefixPatternMatch() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final LookupRegistry matcher = new UriPatternMatcher<>(); matcher.register("/ma*", h1); matcher.register("*tch", h2); final Object h = matcher.lookup("/match"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); } @Test public void testRegisterInvalidInput() throws Exception { final LookupRegistry matcher = new UriPatternMatcher<>(); Assertions.assertThrows(NullPointerException.class, () -> matcher.register(null, null)); } @Test public void testLookupInvalidInput() throws Exception { final LookupRegistry matcher = new UriPatternMatcher<>(); Assertions.assertThrows(NullPointerException.class, () -> matcher.lookup(null)); } @Test public void testMatchExact() { final Object h1 = new Object(); final Object h2 = new Object(); final LookupRegistry matcher = new UriPatternMatcher<>(); matcher.register("exact", h1); matcher.register("*", h2); final Object h = matcher.lookup("exact"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); } } httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriPatternOrderedMatcher.java0100664 0000000 0000000 00000015724 14403631147 031077 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestUriPatternOrderedMatcher { @Test public void testEntrySet() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object h3 = new Object(); final UriPatternOrderedMatcher matcher = new UriPatternOrderedMatcher<>(); Assertions.assertEquals(0, matcher.entrySet().size()); matcher.register("/h1", h1); Assertions.assertEquals(1, matcher.entrySet().size()); matcher.register("/h2", h2); Assertions.assertEquals(2, matcher.entrySet().size()); matcher.register("/h3", h3); Assertions.assertEquals(3, matcher.entrySet().size()); } @Test public void testRegisterUnregister() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object h3 = new Object(); final LookupRegistry matcher = new UriPatternOrderedMatcher<>(); matcher.register("/h1", h1); matcher.register("/h2", h2); matcher.register("/h3", h3); Object h; h = matcher.lookup("/h1"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); h = matcher.lookup("/h2"); Assertions.assertNotNull(h); Assertions.assertSame(h2, h); h = matcher.lookup("/h3"); Assertions.assertNotNull(h); Assertions.assertSame(h3, h); matcher.unregister("/h1"); h = matcher.lookup("/h1"); Assertions.assertNull(h); } @Test public void testRegisterNull() throws Exception { final LookupRegistry matcher = new UriPatternOrderedMatcher<>(); Assertions.assertThrows(NullPointerException.class, () -> matcher.register(null, null)); } @Test public void testWildCardMatching1() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object h3 = new Object(); final Object def = new Object(); final LookupRegistry matcher = new UriPatternOrderedMatcher<>(); matcher.register("*", def); matcher.register("/one/*", h1); matcher.register("/one/two/*", h2); matcher.register("/one/two/three/*", h3); Object h; h = matcher.lookup("/one/request"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); h = matcher.lookup("/one/two/request"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); h = matcher.lookup("/one/two/three/request"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); h = matcher.lookup("default/request"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); } @Test public void testWildCardMatching2() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final Object def = new Object(); final LookupRegistry matcher = new UriPatternOrderedMatcher<>(); matcher.register("*", def); matcher.register("*.view", h1); matcher.register("*.form", h2); Object h; h = matcher.lookup("/that.view"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); h = matcher.lookup("/that.form"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); h = matcher.lookup("/whatever"); Assertions.assertNotNull(h); Assertions.assertSame(def, h); } @Test public void testSuffixPatternOverPrefixPatternMatch() throws Exception { final Object h1 = new Object(); final Object h2 = new Object(); final LookupRegistry matcher = new UriPatternOrderedMatcher<>(); matcher.register("/ma*", h1); matcher.register("*tch", h2); final Object h = matcher.lookup("/match"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); } @Test public void testRegisterInvalidInput() throws Exception { final LookupRegistry matcher = new UriPatternOrderedMatcher<>(); Assertions.assertThrows(NullPointerException.class, () -> matcher.register(null, null)); } @Test public void testLookupInvalidInput() throws Exception { final LookupRegistry matcher = new UriPatternOrderedMatcher<>(); Assertions.assertThrows(NullPointerException.class, () -> matcher.lookup(null)); } @Test public void testMatchExact() { final Object h1 = new Object(); final Object h2 = new Object(); final LookupRegistry matcher = new UriPatternOrderedMatcher<>(); matcher.register("exact", h1); matcher.register("*", h2); final Object h = matcher.lookup("exact"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); } @Test public void testStarAndExact() { final Object h1 = new Object(); final Object h2 = new Object(); final LookupRegistry matcher = new UriPatternOrderedMatcher<>(); matcher.register("*", h1); matcher.register("exact", h2); final Object h = matcher.lookup("exact"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); } @Test public void testExactAndStar() { final Object h1 = new Object(); final Object h2 = new Object(); final LookupRegistry matcher = new UriPatternOrderedMatcher<>(); matcher.register("exact", h1); matcher.register("*", h2); final Object h = matcher.lookup("exact"); Assertions.assertNotNull(h); Assertions.assertSame(h1, h); } } httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestStandardInterceptors.java0100664 0000000 0000000 00000145373 14403631147 030337 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.io.entity.BasicHttpEntity; import org.apache.hc.core5.http.io.entity.EmptyInputStream; import org.apache.hc.core5.http.io.entity.HttpEntities; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.net.URIAuthority; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestStandardInterceptors { @Test public void testRequestConnControlGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); final HttpRequestInterceptor interceptor = RequestConnControl.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals("keep-alive", header.getValue()); } @Test public void testRequestConnControlConnectMethod() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.CONNECT, "/"); final HttpRequestInterceptor interceptor = RequestConnControl.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNull(header); } @Test public void testRequestConnControlCustom() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); final Header myheader = new BasicHeader(HttpHeaders.CONNECTION, "close"); request.addHeader(myheader); final HttpRequestInterceptor interceptor = RequestConnControl.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals("close", header.getValue()); Assertions.assertSame(header, myheader); } @Test public void testRequestConnControlUpgrade() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.addHeader(HttpHeaders.UPGRADE, "HTTP/2"); final HttpRequestInterceptor interceptor = RequestConnControl.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals("upgrade", header.getValue()); } @Test public void testRequestConnControlInvalidInput() throws Exception { final HttpRequestInterceptor interceptor = RequestConnControl.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, null)); } @Test public void testRequestContentProtocolException() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/"); request1.addHeader(new BasicHeader(HttpHeaders.TRANSFER_ENCODING, "chunked")); final BasicClassicHttpRequest request2 = new BasicClassicHttpRequest(Method.POST, "/"); request2.addHeader(new BasicHeader(HttpHeaders.CONTENT_LENGTH, "12")); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; Assertions.assertThrows(ProtocolException.class, () -> interceptor.process(request1, request1.getEntity(), context)); Assertions.assertThrows(ProtocolException.class, () -> interceptor.process(request2, request2.getEntity(), context)); } @Test public void testRequestContentNullEntity() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.CONTENT_LENGTH); Assertions.assertNull(header); Assertions.assertNull(request.getFirstHeader(HttpHeaders.TRANSFER_ENCODING)); } @Test public void testRequestContentEntityContentLengthDelimitedHTTP11() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new StringEntity("whatever", StandardCharsets.US_ASCII)); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.CONTENT_LENGTH); Assertions.assertNotNull(header); Assertions.assertEquals(8, Integer.parseInt(header.getValue())); Assertions.assertNull(request.getFirstHeader(HttpHeaders.TRANSFER_ENCODING)); } @Test public void testRequestContentEntityChunkedHTTP11() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new StringEntity("whatever", StandardCharsets.US_ASCII, true)); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); Assertions.assertNotNull(header); Assertions.assertEquals("chunked", header.getValue()); Assertions.assertNull(request.getFirstHeader(HttpHeaders.CONTENT_LENGTH)); } @Test public void testRequestContentEntityUnknownLengthHTTP11() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, -1, null)); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); Assertions.assertNotNull(header); Assertions.assertEquals("chunked", header.getValue()); Assertions.assertNull(request.getFirstHeader(HttpHeaders.CONTENT_LENGTH)); } @Test public void testRequestContentEntityChunkedHTTP10() throws Exception { final HttpContext context = new BasicHttpContext(null); context.setProtocolVersion(HttpVersion.HTTP_1_0); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new StringEntity("whatever", StandardCharsets.US_ASCII, true)); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; Assertions.assertThrows(ProtocolException.class, () -> interceptor.process(request, request.getEntity(), context)); } @Test public void testRequestContentTypeAndEncoding() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, ContentType.parseLenient("whatever"), "whatever")); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header h1 = request.getFirstHeader(HttpHeaders.CONTENT_TYPE); Assertions.assertNotNull(h1); Assertions.assertEquals("whatever", h1.getValue()); final Header h2 = request.getFirstHeader(HttpHeaders.CONTENT_ENCODING); Assertions.assertNotNull(h2); Assertions.assertEquals("whatever", h2.getValue()); } @Test public void testRequestContentNullTypeAndEncoding() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null, null)); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; interceptor.process(request, request.getEntity(), context); Assertions.assertNull(request.getFirstHeader(HttpHeaders.CONTENT_TYPE)); Assertions.assertNull(request.getFirstHeader(HttpHeaders.CONTENT_ENCODING)); } @Test public void testRequestContentEntityUnknownLengthHTTP10() throws Exception { final HttpContext context = new BasicHttpContext(null); context.setProtocolVersion(HttpVersion.HTTP_1_0); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, -1, null)); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; Assertions.assertThrows(ProtocolException.class, () -> interceptor.process(request, request.getEntity(), context)); } @Test public void testRequestContentInvalidInput() throws Exception { final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, null)); } @Test public void testRequestContentIgnoreNonenclosingRequests() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; interceptor.process(request, request.getEntity(), context); Assertions.assertEquals(0, request.getHeaders().length); } @Test public void testRequestContentOverwriteHeaders() throws Exception { final RequestContent interceptor = new RequestContent(true); final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.addHeader(new BasicHeader(HttpHeaders.CONTENT_LENGTH, "10")); request.addHeader(new BasicHeader(HttpHeaders.TRANSFER_ENCODING, "whatever")); request.setEntity(new StringEntity("")); interceptor.process(request, request.getEntity(), context); final Header h1 = request.getFirstHeader(HttpHeaders.CONTENT_LENGTH); Assertions.assertNotNull(h1); Assertions.assertEquals("0", h1.getValue()); } @Test public void testRequestContentAddHeaders() throws Exception { final RequestContent interceptor = new RequestContent(true); final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new StringEntity("")); interceptor.process(request, request.getEntity(), context); final Header h1 = request.getFirstHeader(HttpHeaders.CONTENT_LENGTH); Assertions.assertNotNull(h1); Assertions.assertEquals("0", h1.getValue()); final Header h2 = request.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); Assertions.assertNull(h2); } @Test public void testRequestContentEntityWithTrailers() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(HttpEntities.create("whatever", StandardCharsets.US_ASCII, new BasicHeader("h1", "this"), new BasicHeader("h1", "that"), new BasicHeader("h2", "this and that"))); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header1 = request.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); Assertions.assertNotNull(header1); final Header header2 = request.getFirstHeader(HttpHeaders.TRAILER); Assertions.assertNotNull(header2); Assertions.assertEquals("h1, h2", header2.getValue()); } @Test public void testRequestContentTraceWithEntity() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.TRACE, "/"); request.setEntity(new StringEntity("stuff")); final HttpRequestInterceptor interceptor = RequestContent.INSTANCE; Assertions.assertThrows(ProtocolException.class, () -> interceptor.process(request, request.getEntity(), context)); } @Test public void testRequestExpectContinueGenerated() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new StringEntity("whatever", StandardCharsets.US_ASCII)); final RequestExpectContinue interceptor = RequestExpectContinue.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.EXPECT); Assertions.assertNotNull(header); Assertions.assertEquals(HeaderElements.CONTINUE, header.getValue()); } @Test public void testRequestExpectContinueHTTP10() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); context.setProtocolVersion(HttpVersion.HTTP_1_0); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new StringEntity("whatever", StandardCharsets.US_ASCII)); final RequestExpectContinue interceptor = RequestExpectContinue.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.EXPECT); Assertions.assertNull(header); } @Test public void testRequestExpectContinueZeroContent() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new StringEntity("", StandardCharsets.US_ASCII)); final RequestExpectContinue interceptor = RequestExpectContinue.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.EXPECT); Assertions.assertNull(header); } @Test public void testRequestExpectContinueInvalidInput() throws Exception { final RequestExpectContinue interceptor = RequestExpectContinue.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, null)); } @Test public void testRequestExpectContinueIgnoreNonenclosingRequests() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); final RequestExpectContinue interceptor = RequestExpectContinue.INSTANCE; interceptor.process(request, request.getEntity(), context); Assertions.assertEquals(0, request.getHeaders().length); } @Test public void testRequestTargetHostGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.setAuthority(new URIAuthority("somehost", 8080)); final HttpRequestInterceptor interceptor = RequestTargetHost.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.HOST); Assertions.assertNotNull(header); Assertions.assertEquals("somehost:8080", header.getValue()); } @Test public void testRequestTargetHostNotGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.setAuthority(new URIAuthority("somehost", 8080)); request.addHeader(new BasicHeader(HttpHeaders.HOST, "whatever")); final HttpRequestInterceptor interceptor = RequestTargetHost.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.HOST); Assertions.assertNotNull(header); Assertions.assertEquals("whatever", header.getValue()); } @Test public void testRequestTargetHostMissingHostHTTP10() throws Exception { final HttpContext context = new BasicHttpContext(null); context.setProtocolVersion(HttpVersion.HTTP_1_0); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); final HttpRequestInterceptor interceptor = RequestTargetHost.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.HOST); Assertions.assertNull(header); } @Test public void testRequestTargetHostMissingHostHTTP11() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); final HttpRequestInterceptor interceptor = RequestTargetHost.INSTANCE; Assertions.assertThrows(ProtocolException.class, () -> interceptor.process(request, request.getEntity(), context)); } @Test public void testRequestTargetHostInvalidInput() throws Exception { final HttpRequestInterceptor interceptor = RequestTargetHost.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, null)); Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(new BasicClassicHttpRequest(Method.GET, "/"), null, null)); } @Test public void testRequestTargetHostConnectHttp11() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.CONNECT, "/"); request.setAuthority(new URIAuthority("somehost", 8080)); final HttpRequestInterceptor interceptor = RequestTargetHost.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.HOST); Assertions.assertNotNull(header); Assertions.assertEquals("somehost:8080", header.getValue()); } @Test public void testRequestTargetHostConnectHttp10() throws Exception { final HttpContext context = new BasicHttpContext(null); context.setProtocolVersion(HttpVersion.HTTP_1_0); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.CONNECT, "/"); request.setAuthority(new URIAuthority("somehost", 8080)); final HttpRequestInterceptor interceptor = RequestTargetHost.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.HOST); Assertions.assertNull(header); } @Test public void testRequestUserAgentGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); final RequestUserAgent interceptor = new RequestUserAgent("some agent"); interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.USER_AGENT); Assertions.assertNotNull(header); Assertions.assertEquals("some agent", header.getValue()); } @Test public void testRequestUserAgentNotGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.addHeader(new BasicHeader(HttpHeaders.USER_AGENT, "whatever")); final RequestUserAgent interceptor = new RequestUserAgent("some agent"); interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.USER_AGENT); Assertions.assertNotNull(header); Assertions.assertEquals("whatever", header.getValue()); } @Test public void testRequestUserAgentMissingUserAgent() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); final HttpRequestInterceptor interceptor = RequestUserAgent.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.USER_AGENT); Assertions.assertNull(header); } @Test public void testRequestUserAgentInvalidInput() throws Exception { final HttpRequestInterceptor interceptor = RequestUserAgent.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, null)); } @Test public void testResponseConnControlNoEntity() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); final ResponseConnControl interceptor = new ResponseConnControl(); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNull(header); } @Test public void testResponseConnControlEntityContentLength() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new StringEntity("whatever")); final ResponseConnControl interceptor = new ResponseConnControl(); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNull(header); } @Test public void testResponseConnControlEntityUnknownContentLengthExplicitKeepAlive() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.addHeader(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive")); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null)); final ResponseConnControl interceptor = new ResponseConnControl(); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals("keep-alive", header.getValue()); } @Test public void testResponseConnControlEntityChunked() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null, true)); final ResponseConnControl interceptor = new ResponseConnControl(); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNull(header); } @Test public void testResponseConnControlEntityUnknownContentLengthHTTP10() throws Exception { final HttpContext context = new BasicHttpContext(null); context.setProtocolVersion(HttpVersion.HTTP_1_0); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.addHeader(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive")); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); final BasicClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null)); final ResponseConnControl interceptor = new ResponseConnControl(); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals("close", header.getValue()); } @Test public void testResponseConnControlClientRequest() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.addHeader(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive")); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new StringEntity("whatever")); final ResponseConnControl interceptor = new ResponseConnControl(); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals("keep-alive", header.getValue()); } @Test public void testResponseConnControlClientRequest2() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new StringEntity("whatever")); final ResponseConnControl interceptor = new ResponseConnControl(); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNull(header); } @Test public void testResponseConnControl10Client11Response() throws Exception { final HttpContext context = new BasicHttpContext(null); context.setProtocolVersion(HttpVersion.HTTP_1_0); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new StringEntity("whatever")); final ResponseConnControl interceptor = new ResponseConnControl(); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals("close", header.getValue()); } @Test public void testResponseConnControlStatusCode() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.addHeader(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive")); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); final ResponseConnControl interceptor = new ResponseConnControl(); final int [] statusCodes = new int[] { HttpStatus.SC_BAD_REQUEST, HttpStatus.SC_REQUEST_TIMEOUT, HttpStatus.SC_LENGTH_REQUIRED, HttpStatus.SC_REQUEST_TOO_LONG, HttpStatus.SC_REQUEST_URI_TOO_LONG, HttpStatus.SC_SERVICE_UNAVAILABLE, HttpStatus.SC_NOT_IMPLEMENTED }; for (final int statusCode : statusCodes) { final ClassicHttpResponse response = new BasicClassicHttpResponse(statusCode, "Unreasonable"); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals("close", header.getValue()); } } @Test public void testResponseConnControlExplicitClose() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.addHeader(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive")); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); final ResponseConnControl interceptor = new ResponseConnControl(); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); response.setHeader(HttpHeaders.CONNECTION, "close"); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals("close", header.getValue()); } @Test public void testResponseConnControlClientRequestMixUp() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.addHeader(new BasicHeader(HttpHeaders.CONNECTION, "blah, keep-alive, close")); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); final ResponseConnControl interceptor = new ResponseConnControl(); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals("close", header.getValue()); } @Test public void testResponseConnControlUpgrade() throws Exception { final HttpContext context = new BasicHttpContext(null); final ResponseConnControl interceptor = new ResponseConnControl(); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); response.addHeader(HttpHeaders.UPGRADE, "HTTP/2"); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals("upgrade", header.getValue()); } @Test public void testResponseConnControlHostInvalidInput() throws Exception { final ResponseConnControl interceptor = new ResponseConnControl(); Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, null)); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(response, null, null)); } @Test public void testResponseContentNoEntity() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); final ResponseContent interceptor = new ResponseContent(); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); Assertions.assertNotNull(header); Assertions.assertEquals("0", header.getValue()); } @Test public void testResponseContentStatusNoContent() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setCode(HttpStatus.SC_NO_CONTENT); final ResponseContent interceptor = new ResponseContent(); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); Assertions.assertNull(header); } @Test public void testResponseContentStatusNotModified() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setCode(HttpStatus.SC_NOT_MODIFIED); final ResponseContent interceptor = new ResponseContent(); interceptor.process(response, response.getEntity(), context); final Header header = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); Assertions.assertNull(header); } @Test public void testResponseContentEntityChunked() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null, true)); final ResponseContent interceptor = new ResponseContent(); interceptor.process(response, response.getEntity(), context); final Header h1 = response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); Assertions.assertNotNull(h1); Assertions.assertEquals("chunked", h1.getValue()); final Header h2 = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); Assertions.assertNull(h2); } @Test public void testResponseContentEntityContentLenghtDelimited() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, 10, null)); final ResponseContent interceptor = new ResponseContent(); interceptor.process(response, response.getEntity(), context); final Header h1 = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); Assertions.assertNotNull(h1); Assertions.assertEquals("10", h1.getValue()); final Header h2 = response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); Assertions.assertNull(h2); } @Test public void testResponseContentEntityUnknownContentLength() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null)); final ResponseContent interceptor = new ResponseContent(); interceptor.process(response, response.getEntity(), context); final Header h1 = response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); Assertions.assertNotNull(h1); Assertions.assertEquals("chunked", h1.getValue()); final Header h2 = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); Assertions.assertNull(h2); } @Test public void testResponseContentEntityChunkedHTTP10() throws Exception { final HttpContext context = new BasicHttpContext(null); context.setProtocolVersion(HttpVersion.HTTP_1_0); final BasicClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null, true)); final ResponseContent interceptor = new ResponseContent(); interceptor.process(response, response.getEntity(), context); final Header h1 = response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); Assertions.assertNull(h1); final Header h2 = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); Assertions.assertNull(h2); } @Test public void testResponseContentEntityNoContentTypeAndEncoding() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null)); final ResponseContent interceptor = new ResponseContent(); interceptor.process(response, response.getEntity(), context); final Header h1 = response.getFirstHeader(HttpHeaders.CONTENT_TYPE); Assertions.assertNull(h1); final Header h2 = response.getFirstHeader(HttpHeaders.CONTENT_ENCODING); Assertions.assertNull(h2); } @Test public void testResponseContentEntityContentTypeAndEncoding() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, ContentType.parseLenient("whatever"), "whatever")); final ResponseContent interceptor = new ResponseContent(); interceptor.process(response, response.getEntity(), context); final Header h1 = response.getFirstHeader(HttpHeaders.CONTENT_TYPE); Assertions.assertNotNull(h1); Assertions.assertEquals("whatever", h1.getValue()); final Header h2 = response.getFirstHeader(HttpHeaders.CONTENT_ENCODING); Assertions.assertNotNull(h2); Assertions.assertEquals("whatever", h2.getValue()); } @Test public void testResponseContentInvalidInput() throws Exception { final ResponseContent interceptor = new ResponseContent(); Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, null)); } @Test public void testResponseContentInvalidResponseState() throws Exception { final ResponseContent interceptor = new ResponseContent(); final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response1.addHeader(new BasicHeader(HttpHeaders.CONTENT_LENGTH, "10")); Assertions.assertThrows(ProtocolException.class, () -> interceptor.process(response1, response1.getEntity(), context)); final ClassicHttpResponse response2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response2.addHeader(new BasicHeader(HttpHeaders.TRANSFER_ENCODING, "stuff")); Assertions.assertThrows(ProtocolException.class, () -> interceptor.process(response2, response2.getEntity(), context)); } @Test public void testResponseContentOverwriteHeaders() throws Exception { final ResponseContent interceptor = new ResponseContent(true); final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.addHeader(new BasicHeader(HttpHeaders.CONTENT_LENGTH, "10")); response.addHeader(new BasicHeader(HttpHeaders.TRANSFER_ENCODING, "whatever")); interceptor.process(response, response.getEntity(), context); Assertions.assertEquals("0", response.getFirstHeader(HttpHeaders.CONTENT_LENGTH).getValue()); } @Test public void testResponseContentAddHeaders() throws Exception { final ResponseContent interceptor = new ResponseContent(true); final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); interceptor.process(response, response.getEntity(), context); Assertions.assertEquals("0", response.getFirstHeader(HttpHeaders.CONTENT_LENGTH).getValue()); Assertions.assertNull(response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING)); } @Test public void testResponseContentEntityWithTrailers() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setEntity(HttpEntities.create("whatever", StandardCharsets.US_ASCII, new BasicHeader("h1", "this"), new BasicHeader("h1", "that"), new BasicHeader("h2", "this and that"))); final ResponseContent interceptor = new ResponseContent(); interceptor.process(response, response.getEntity(), context); final Header header1 = response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); Assertions.assertNotNull(header1); final Header header2 = response.getFirstHeader(HttpHeaders.TRAILER); Assertions.assertNotNull(header2); Assertions.assertEquals("h1, h2", header2.getValue()); } @Test public void testResponseDateGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); final ResponseDate interceptor = new ResponseDate(); interceptor.process(response, response.getEntity(), context); final Header h1 = response.getFirstHeader(HttpHeaders.DATE); Assertions.assertNotNull(h1); interceptor.process(response, response.getEntity(), context); final Header h2 = response.getFirstHeader(HttpHeaders.DATE); Assertions.assertNotNull(h2); } @Test public void testResponseDateNotGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.setCode(199); final ResponseDate interceptor = new ResponseDate(); interceptor.process(response, response.getEntity(), context); final Header h1 = response.getFirstHeader(HttpHeaders.DATE); Assertions.assertNull(h1); } @Test public void testResponseDateInvalidInput() throws Exception { final ResponseDate interceptor = new ResponseDate(); Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, null)); } @Test public void testRequestDateGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.setEntity(new StringEntity("stuff")); final HttpRequestInterceptor interceptor = RequestDate.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header h1 = request.getFirstHeader(HttpHeaders.DATE); Assertions.assertNotNull(h1); interceptor.process(request, request.getEntity(), context); final Header h2 = request.getFirstHeader(HttpHeaders.DATE); Assertions.assertNotNull(h2); } @Test public void testRequestDateNotGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); final HttpRequestInterceptor interceptor = RequestDate.INSTANCE; interceptor.process(request, request.getEntity(), context); final Header h1 = request.getFirstHeader(HttpHeaders.DATE); Assertions.assertNull(h1); } @Test public void testRequestDateInvalidInput() throws Exception { final HttpRequestInterceptor interceptor = RequestDate.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, null)); } @Test public void testResponseServerGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); final ResponseServer interceptor = new ResponseServer("some server"); interceptor.process(response, response.getEntity(), context); final Header h1 = response.getFirstHeader(HttpHeaders.SERVER); Assertions.assertNotNull(h1); Assertions.assertEquals("some server", h1.getValue()); } @Test public void testResponseServerNotGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); response.addHeader(new BasicHeader(HttpHeaders.SERVER, "whatever")); final ResponseServer interceptor = new ResponseServer("some server"); interceptor.process(response, response.getEntity(), context); final Header h1 = response.getFirstHeader(HttpHeaders.SERVER); Assertions.assertNotNull(h1); Assertions.assertEquals("whatever", h1.getValue()); } @Test public void testResponseServerMissing() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); final ResponseServer interceptor = new ResponseServer(); interceptor.process(response, response.getEntity(), context); final Header h1 = response.getFirstHeader(HttpHeaders.SERVER); Assertions.assertNull(h1); } @Test public void testResponseServerInvalidInput() throws Exception { final ResponseServer interceptor = new ResponseServer(); Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, null)); } @Test public void testRequestHttp10HostHeaderAbsent() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.setVersion(HttpVersion.HTTP_1_0); final RequestValidateHost interceptor = new RequestValidateHost(); interceptor.process(request, request.getEntity(), context); } @Test public void testRequestHttpHostHeader() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.setVersion(HttpVersion.HTTP_1_1); request.setHeader(HttpHeaders.HOST, "host:8888"); final RequestValidateHost interceptor = new RequestValidateHost(); interceptor.process(request, request.getEntity(), context); Assertions.assertEquals(new URIAuthority("host", 8888), request.getAuthority()); } @Test public void testRequestHttpHostHeaderNoPort() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.setVersion(HttpVersion.HTTP_1_1); request.setHeader(HttpHeaders.HOST, "host"); final RequestValidateHost interceptor = new RequestValidateHost(); interceptor.process(request, request.getEntity(), context); Assertions.assertEquals(new URIAuthority("host"), request.getAuthority()); } @Test public void testRequestHttp11HostHeaderPresent() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.setHeader(HttpHeaders.HOST, "blah"); final RequestValidateHost interceptor = new RequestValidateHost(); interceptor.process(request, request.getEntity(), context); } @Test public void testRequestHttp11HostHeaderAbsent() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); final RequestValidateHost interceptor = new RequestValidateHost(); Assertions.assertThrows(ProtocolException.class, () -> interceptor.process(request, request.getEntity(), context)); } @Test public void testRequestHttp11MultipleHostHeaders() throws Exception { final HttpContext context = new BasicHttpContext(null); final BasicClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); request.addHeader(HttpHeaders.HOST, "blah"); request.addHeader(HttpHeaders.HOST, "blah"); final RequestValidateHost interceptor = new RequestValidateHost(); Assertions.assertThrows(ProtocolException.class, () -> interceptor.process(request, request.getEntity(), context)); } } httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestChainBuilder.java0100664 0000000 0000000 00000005177 14403631147 026523 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.protocol; import java.util.LinkedList; import java.util.List; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestChainBuilder { @Test public void testBuildChain() throws Exception { final ChainBuilder cb = new ChainBuilder<>(); final HttpRequestInterceptor i1 = RequestContent.INSTANCE; final HttpRequestInterceptor i2 = RequestTargetHost.INSTANCE; final HttpRequestInterceptor i3 = RequestConnControl.INSTANCE; final HttpRequestInterceptor i4 = RequestUserAgent.INSTANCE; final HttpRequestInterceptor i5 = RequestExpectContinue.INSTANCE; cb.addFirst(i1); cb.addAllFirst(i2, i3); cb.addFirst(null); cb.addAllFirst((List) null); cb.addLast(i4); cb.addLast(null); cb.addAllLast(i5); cb.addAllLast((List) null); cb.addFirst(i1); cb.addAllLast(i3, i4, i5); final LinkedList list = cb.build(); Assertions.assertNotNull(list); Assertions.assertEquals(5, list.size()); Assertions.assertSame(i1, list.get(0)); Assertions.assertSame(i2, list.get(1)); Assertions.assertSame(i3, list.get(2)); Assertions.assertSame(i4, list.get(3)); Assertions.assertSame(i5, list.get(4)); } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/0040775 0000000 0000000 00000000000 14403631147 021404 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/nio/support/0040775 0000000 0000000 00000000000 14245617503 023124 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/nio/support/classic/0040775 0000000 0000000 00000000000 14411341112 024526 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/nio/support/classic/TestSharedOutputBuffer.java0100664 0000000 0000000 00000020140 14403631147 032017 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.WritableByteChannelMock; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestSharedOutputBuffer { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); static class DataStreamChannelMock implements DataStreamChannel { private final WritableByteChannelMock channel; DataStreamChannelMock(final WritableByteChannelMock channel) { this.channel = channel; } @Override public synchronized int write(final ByteBuffer src) throws IOException { return channel.write(src); } @Override public synchronized void requestOutput() { notifyAll(); } @Override public synchronized void endStream(final List trailers) throws IOException { channel.close(); notifyAll(); } @Override public void endStream() throws IOException { endStream(null); } public synchronized void awaitOutputRequest() throws InterruptedException { wait(); } } @Test public void testBasis() throws Exception { final Charset charset = StandardCharsets.US_ASCII; final SharedOutputBuffer outputBuffer = new SharedOutputBuffer(30); final WritableByteChannelMock channel = new WritableByteChannelMock(1024); final DataStreamChannel dataStreamChannel = Mockito.spy(new DataStreamChannelMock(channel)); outputBuffer.flush(dataStreamChannel); Mockito.verifyNoInteractions(dataStreamChannel); Assertions.assertEquals(0, outputBuffer.length()); Assertions.assertEquals(30, outputBuffer.capacity()); final byte[] tmp = "1234567890".getBytes(charset); outputBuffer.write(tmp, 0, tmp.length); outputBuffer.write(tmp, 0, tmp.length); outputBuffer.write('1'); outputBuffer.write('2'); Assertions.assertEquals(22, outputBuffer.length()); Assertions.assertEquals(8, outputBuffer.capacity()); Mockito.verifyNoInteractions(dataStreamChannel); } @Test public void testFlush() throws Exception { final Charset charset = StandardCharsets.US_ASCII; final SharedOutputBuffer outputBuffer = new SharedOutputBuffer(30); final WritableByteChannelMock channel = new WritableByteChannelMock(1024); final DataStreamChannel dataStreamChannel = new DataStreamChannelMock(channel); outputBuffer.flush(dataStreamChannel); Assertions.assertEquals(0, outputBuffer.length()); Assertions.assertEquals(30, outputBuffer.capacity()); final byte[] tmp = "1234567890".getBytes(charset); outputBuffer.write(tmp, 0, tmp.length); outputBuffer.write(tmp, 0, tmp.length); outputBuffer.write('1'); outputBuffer.write('2'); outputBuffer.flush(dataStreamChannel); Assertions.assertEquals(0, outputBuffer.length()); Assertions.assertEquals(30, outputBuffer.capacity()); } @Test public void testMultithreadingWriteStream() throws Exception { final Charset charset = StandardCharsets.US_ASCII; final SharedOutputBuffer outputBuffer = new SharedOutputBuffer(20); final WritableByteChannelMock channel = new WritableByteChannelMock(1024); final DataStreamChannelMock dataStreamChannel = new DataStreamChannelMock(channel); final ExecutorService executorService = Executors.newFixedThreadPool(2); final Future task1 = executorService.submit(() -> { final byte[] tmp = "1234567890".getBytes(charset); outputBuffer.write(tmp, 0, tmp.length); outputBuffer.write(tmp, 0, tmp.length); outputBuffer.write('1'); outputBuffer.write('2'); outputBuffer.write(tmp, 0, tmp.length); outputBuffer.write(tmp, 0, tmp.length); outputBuffer.write(tmp, 0, tmp.length); outputBuffer.writeCompleted(); outputBuffer.writeCompleted(); return Boolean.TRUE; }); final Future task2 = executorService.submit(() -> { for (;;) { outputBuffer.flush(dataStreamChannel); if (outputBuffer.isEndStream()) { break; } if (!outputBuffer.hasData()) { dataStreamChannel.awaitOutputRequest(); } } return Boolean.TRUE; }); Assertions.assertEquals(Boolean.TRUE, task1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); Assertions.assertEquals(Boolean.TRUE, task2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); Assertions.assertEquals("1234567890123456789012123456789012345678901234567890", new String(channel.toByteArray(), charset)); } @Test public void testMultithreadingWriteStreamAbort() throws Exception { final Charset charset = StandardCharsets.US_ASCII; final SharedOutputBuffer outputBuffer = new SharedOutputBuffer(20); final ExecutorService executorService = Executors.newFixedThreadPool(2); final Future task1 = executorService.submit(() -> { final byte[] tmp = "1234567890".getBytes(charset); for (int i = 0; i < 20; i++) { outputBuffer.write(tmp, 0, tmp.length); } outputBuffer.writeCompleted(); return Boolean.TRUE; }); final Future task2 = executorService.submit(() -> { Thread.sleep(200); outputBuffer.abort(); return Boolean.TRUE; }); Assertions.assertEquals(Boolean.TRUE, task2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); try { task1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); } catch (final ExecutionException ex) { Assertions.assertTrue(ex.getCause() instanceof InterruptedIOException); } } @Test public void testEndStreamOnlyCalledOnce() throws IOException { final DataStreamChannel channel = Mockito.mock(DataStreamChannel.class); final SharedOutputBuffer outputBuffer = new SharedOutputBuffer(20); outputBuffer.flush(channel); outputBuffer.writeCompleted(); outputBuffer.flush(channel); Mockito.verify(channel, Mockito.times(1)).endStream(); } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/support/classic/TestSharedInputBuffer.java0100664 0000000 0000000 00000020343 14411341112 031610 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.support.classic; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; public class TestSharedInputBuffer { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @Test public void testBasis() throws Exception { final Charset charset = StandardCharsets.US_ASCII; final SharedInputBuffer inputBuffer = new SharedInputBuffer(10); inputBuffer.fill(charset.encode("1234567890")); Assertions.assertEquals(10, inputBuffer.length()); final CapacityChannel capacityChannel = Mockito.mock(CapacityChannel.class); inputBuffer.updateCapacity(capacityChannel); Mockito.verifyNoInteractions(capacityChannel); inputBuffer.fill(charset.encode("1234567890")); inputBuffer.fill(charset.encode("1234567890")); Assertions.assertEquals(30, inputBuffer.length()); Mockito.verifyNoInteractions(capacityChannel); final byte[] tmp = new byte[20]; final int bytesRead1 = inputBuffer.read(tmp, 0, tmp.length); Assertions.assertEquals(20, bytesRead1); Mockito.verifyNoInteractions(capacityChannel); inputBuffer.markEndStream(); Assertions.assertEquals('1', inputBuffer.read()); Assertions.assertEquals('2', inputBuffer.read()); final int bytesRead2 = inputBuffer.read(tmp, 0, tmp.length); Assertions.assertEquals(8, bytesRead2); Mockito.verifyNoInteractions(capacityChannel); Assertions.assertEquals(-1, inputBuffer.read(tmp, 0, tmp.length)); Assertions.assertEquals(-1, inputBuffer.read(tmp, 0, tmp.length)); Assertions.assertEquals(-1, inputBuffer.read()); Assertions.assertEquals(-1, inputBuffer.read()); } @Test public void testMultithreadingRead() throws Exception { final SharedInputBuffer inputBuffer = new SharedInputBuffer(10); final CapacityChannel capacityChannel = Mockito.mock(CapacityChannel.class); inputBuffer.updateCapacity(capacityChannel); Mockito.verify(capacityChannel).update(10); Mockito.reset(capacityChannel); final ExecutorService executorService = Executors.newFixedThreadPool(2); final Future task1 = executorService.submit(() -> { final Charset charset = StandardCharsets.US_ASCII; inputBuffer.fill(charset.encode("1234567890")); return Boolean.TRUE; }); final Future task2 = executorService.submit(() -> { final byte[] tmp = new byte[20]; return inputBuffer.read(tmp, 0, tmp.length); }); Assertions.assertEquals(Boolean.TRUE, task1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); Assertions.assertEquals(Integer.valueOf(10), task2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); Mockito.verify(capacityChannel).update(10); } @Test public void testMultithreadingSingleRead() throws Exception { final SharedInputBuffer inputBuffer = new SharedInputBuffer(10); final CapacityChannel capacityChannel = Mockito.mock(CapacityChannel.class); inputBuffer.updateCapacity(capacityChannel); Mockito.verify(capacityChannel).update(10); Mockito.reset(capacityChannel); final ExecutorService executorService = Executors.newFixedThreadPool(2); final Future task1 = executorService.submit(() -> { final Charset charset = StandardCharsets.US_ASCII; inputBuffer.fill(charset.encode("a")); return Boolean.TRUE; }); final Future task2 = executorService.submit((Callable) inputBuffer::read); Assertions.assertEquals(Boolean.TRUE, task1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); Assertions.assertEquals(Integer.valueOf('a'), task2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); Mockito.verify(capacityChannel).update(1); } @Test public void testMultithreadingReadStream() throws Exception { final SharedInputBuffer inputBuffer = new SharedInputBuffer(10); final CapacityChannel capacityChannel = Mockito.mock(CapacityChannel.class); inputBuffer.updateCapacity(capacityChannel); Mockito.verify(capacityChannel).update(10); Mockito.reset(capacityChannel); final ExecutorService executorService = Executors.newFixedThreadPool(2); final Future task1 = executorService.submit(() -> { final Charset charset = StandardCharsets.US_ASCII; final Random rnd = new Random(System.currentTimeMillis()); for (int i = 0; i < 5; i++) { inputBuffer.fill(charset.encode("1234567890")); Thread.sleep(rnd.nextInt(250)); } inputBuffer.markEndStream(); return Boolean.TRUE; }); final Future task2 = executorService.submit(() -> { final Charset charset = StandardCharsets.US_ASCII; final StringBuilder buf = new StringBuilder(); final byte[] tmp = new byte[10]; int l; while ((l = inputBuffer.read(tmp, 0, tmp.length)) != -1) { buf.append(charset.decode(ByteBuffer.wrap(tmp, 0, l))); } return buf.toString(); }); Assertions.assertEquals(Boolean.TRUE, task1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); Assertions.assertEquals("12345678901234567890123456789012345678901234567890", task2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); Mockito.verify(capacityChannel, Mockito.atLeast(1)).update(ArgumentMatchers.anyInt()); } @Test public void testMultithreadingReadStreamAbort() throws Exception { final SharedInputBuffer inputBuffer = new SharedInputBuffer(10); final CapacityChannel capacityChannel = Mockito.mock(CapacityChannel.class); inputBuffer.updateCapacity(capacityChannel); Mockito.verify(capacityChannel).update(10); Mockito.reset(capacityChannel); final ExecutorService executorService = Executors.newFixedThreadPool(2); final Future task1 = executorService.submit(() -> { Thread.sleep(1000); inputBuffer.abort(); return Boolean.TRUE; }); final Future task2 = executorService.submit((Callable) inputBuffer::read); Assertions.assertEquals(Boolean.TRUE, task1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); Assertions.assertEquals(Integer.valueOf(-1), task2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); Mockito.verify(capacityChannel, Mockito.never()).update(10); } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/BasicDataStreamChannel.java0100664 0000000 0000000 00000004467 14245617503 026543 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.http.Header; public class BasicDataStreamChannel implements DataStreamChannel { private final WritableByteChannel byteChannel; private List
trailers; public BasicDataStreamChannel(final WritableByteChannel byteChannel) { this.byteChannel = byteChannel; } @Override public void requestOutput() { } @Override public int write(final ByteBuffer src) throws IOException { return byteChannel.write(src); } @Override public void endStream() throws IOException { if (byteChannel.isOpen()) { byteChannel.close(); } } @Override public void endStream(final List trailers) throws IOException { endStream(); if (trailers != null) { this.trailers = new ArrayList<>(); this.trailers.addAll(trailers); } } public List
getTrailers() { return trailers; } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/ssl/0040775 0000000 0000000 00000000000 14403631147 022205 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/nio/ssl/BasicServerTlsStrategyTest.java0100664 0000000 0000000 00000003550 14403631147 030326 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.ssl; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; class BasicServerTlsStrategyTest { @Mock private SSLSessionVerifier sslSessionVerifier; @BeforeEach public void prepareMocks() { MockitoAnnotations.openMocks(this); } @Test void test_valid_tls_strategy_creation_with_verifier() { final BasicServerTlsStrategy strategy = new BasicServerTlsStrategy(sslSessionVerifier); assertNotNull(strategy); } }httpcore5/src/test/java/org/apache/hc/core5/http/nio/ssl/BasicClientTlsStrategyTest.java0100664 0000000 0000000 00000003550 14403631147 030276 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.ssl; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; class BasicClientTlsStrategyTest { @Mock private SSLSessionVerifier sslSessionVerifier; @BeforeEach public void prepareMocks() { MockitoAnnotations.openMocks(this); } @Test void test_valid_tls_strategy_creation_with_verifier() { final BasicClientTlsStrategy strategy = new BasicClientTlsStrategy(sslSessionVerifier); assertNotNull(strategy); } }httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/0040775 0000000 0000000 00000000000 14403631147 022720 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestDigestingEntityConsumer.java0100664 0000000 0000000 00000004000 14403631147 031240 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.nio.ByteBuffer; import org.apache.hc.core5.util.TextUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDigestingEntityConsumer { @Test public void testConsumeData() throws Exception { final DigestingEntityConsumer consumer = new DigestingEntityConsumer<>("MD5", new StringAsyncEntityConsumer()); consumer.consume(ByteBuffer.wrap(new byte[]{'1', '2', '3'})); consumer.consume(ByteBuffer.wrap(new byte[]{'4', '5'})); consumer.consume(ByteBuffer.wrap(new byte[]{})); consumer.streamEnd(null); Assertions.assertEquals("12345", consumer.getContent()); Assertions.assertEquals("827ccb0eea8a706c4c34a16891f84e7b", TextUtils.toHexString(consumer.getDigest())); } } ././@LongLink0100644 0000000 0000000 00000000145 14403631147 011636 Lustar 0000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestAbstractCharAsyncEntityProducer.javahttpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestAbstractCharAsyncEntityProducer.java0100664 0000000 0000000 00000011776 14403631147 032674 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.WritableByteChannelMock; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.BasicDataStreamChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.StreamChannel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestAbstractCharAsyncEntityProducer { static private class ChunkCharAsyncEntityProducer extends AbstractCharAsyncEntityProducer { private final String[] content; private int count = 0; public ChunkCharAsyncEntityProducer( final int bufferSize, final int fragmentSizeHint, final ContentType contentType, final String... content) { super(bufferSize, fragmentSizeHint, contentType); this.content = content; } @Override public boolean isRepeatable() { return false; } @Override protected int availableData() { return Integer.MAX_VALUE; } @Override protected void produceData(final StreamChannel channel) throws IOException { if (count < content.length) { channel.write(CharBuffer.wrap(content[count])); } count++; if (count >= content.length) { channel.endStream(); } } @Override public void failed(final Exception cause) { } } @Test public void testProduceDataNoBuffering() throws Exception { final AsyncEntityProducer producer = new ChunkCharAsyncEntityProducer( 256, 0, ContentType.TEXT_PLAIN, "this", "this and that"); Assertions.assertEquals(-1, producer.getContentLength()); Assertions.assertEquals(ContentType.TEXT_PLAIN.toString(), producer.getContentType()); Assertions.assertNull(producer.getContentEncoding()); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("this", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("this and that", byteChannel.dump(StandardCharsets.US_ASCII)); } @Test public void testProduceDataWithBuffering() throws Exception { final AsyncEntityProducer producer = new ChunkCharAsyncEntityProducer( 256, 5, ContentType.TEXT_PLAIN, "this", " and that", "all", " sorts of stuff"); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("this and that", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("all sorts of stuff", byteChannel.dump(StandardCharsets.US_ASCII)); } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestFileEntityProducer.java0100664 0000000 0000000 00000004077 14403631147 030210 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.file.Files; import java.nio.file.Path; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; /** * @since 5.0 */ public class TestFileEntityProducer { @Test public void testFileLengthMaxIntPlusOne(@TempDir final Path tempFolder) throws IOException { final Path path = Files.createFile(tempFolder.resolve("test.bin")); try (RandomAccessFile raFile = new RandomAccessFile(path.toFile(), "rw")) { final long expectedLength = 1L + Integer.MAX_VALUE; raFile.setLength(expectedLength); final FileEntityProducer fileEntityProducer = new FileEntityProducer(path.toFile()); Assertions.assertEquals(expectedLength, fileEntityProducer.getContentLength()); } } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestPathEntityProducer.java0100664 0000000 0000000 00000004170 14403631147 030217 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; /** * @since 5.0 */ public class TestPathEntityProducer { @Test public void testFileLengthMaxIntPlusOne(@TempDir final Path tempFolder) throws IOException { final Path path = Files.createFile(tempFolder.resolve("test.bin")); try (RandomAccessFile raFile = new RandomAccessFile(path.toFile(), "rw")) { final long expectedLength = 1L + Integer.MAX_VALUE; raFile.setLength(expectedLength); final PathEntityProducer fileEntityProducer = new PathEntityProducer(path, StandardOpenOption.READ); Assertions.assertEquals(expectedLength, fileEntityProducer.getContentLength()); } } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestAbstractBinAsyncEntityConsumer.java0100664 0000000 0000000 00000007402 14403631147 032526 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.impl.BasicEntityDetails; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.util.ByteArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestAbstractBinAsyncEntityConsumer { static private class ByteArrayAsyncEntityConsumer extends AbstractBinAsyncEntityConsumer { private final ByteArrayBuffer buffer; public ByteArrayAsyncEntityConsumer() { super(); this.buffer = new ByteArrayBuffer(1024); } @Override protected void streamStart(final ContentType contentType) throws HttpException, IOException { } @Override protected int capacityIncrement() { return Integer.MAX_VALUE; } @Override protected void data(final ByteBuffer src, final boolean endOfStream) throws IOException { buffer.append(src); } @Override protected byte[] generateContent() throws IOException { return buffer.toByteArray(); } @Override public void releaseResources() { } } @Test public void testConsumeData() throws Exception { final AsyncEntityConsumer consumer = new ByteArrayAsyncEntityConsumer(); final AtomicLong count = new AtomicLong(0); consumer.streamStart(new BasicEntityDetails(-1, ContentType.APPLICATION_OCTET_STREAM), new FutureCallback() { @Override public void completed(final byte[] result) { count.incrementAndGet(); } @Override public void failed(final Exception ex) { count.incrementAndGet(); } @Override public void cancelled() { count.incrementAndGet(); } }); consumer.consume(ByteBuffer.wrap(new byte[]{'1', '2', '3'})); consumer.consume(ByteBuffer.wrap(new byte[]{'4', '5'})); consumer.consume(ByteBuffer.wrap(new byte[]{})); Assertions.assertNull(consumer.getContent()); consumer.streamEnd(null); Assertions.assertArrayEquals(new byte[] {'1', '2', '3', '4', '5'}, consumer.getContent()); Assertions.assertEquals(1L, count.longValue()); } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestAbstractBinAsyncEntityProducer.java0100664 0000000 0000000 00000015722 14403631147 032522 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.WritableByteChannelMock; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.BasicDataStreamChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.StreamChannel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestAbstractBinAsyncEntityProducer { static private class ChunkByteAsyncEntityProducer extends AbstractBinAsyncEntityProducer { private final byte[][] content; private int count = 0; public ChunkByteAsyncEntityProducer( final int fragmentSizeHint, final ContentType contentType, final byte[]... content) { super(fragmentSizeHint, contentType); this.content = content; } @Override public boolean isRepeatable() { return false; } @Override protected int availableData() { return Integer.MAX_VALUE; } @Override protected void produceData(final StreamChannel channel) throws IOException { if (count < content.length) { channel.write(ByteBuffer.wrap(content[count])); } count++; if (count >= content.length) { channel.endStream(); } } @Override public void failed(final Exception cause) { } } @Test public void testProduceDataNoBuffering() throws Exception { final AsyncEntityProducer producer = new ChunkByteAsyncEntityProducer( 0, ContentType.TEXT_PLAIN, new byte[] { '1', '2', '3' }, new byte[] { '4', '5', '6' }); Assertions.assertEquals(-1, producer.getContentLength()); Assertions.assertEquals(ContentType.TEXT_PLAIN.toString(), producer.getContentType()); Assertions.assertNull(producer.getContentEncoding()); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("123", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("456", byteChannel.dump(StandardCharsets.US_ASCII)); } @Test public void testProduceDataWithBuffering1() throws Exception { final AsyncEntityProducer producer = new ChunkByteAsyncEntityProducer( 5, ContentType.TEXT_PLAIN, new byte[] { '1', '2', '3' }, new byte[] { '4', '5', '6' }, new byte[] { '7', '8' }, new byte[] { '9', '0' }); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("123", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("45678", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("90", byteChannel.dump(StandardCharsets.US_ASCII)); } @Test public void testProduceDataWithBuffering2() throws Exception { final AsyncEntityProducer producer = new ChunkByteAsyncEntityProducer( 5, ContentType.TEXT_PLAIN, new byte[] { '1' }, new byte[] { '2' }, new byte[] { '3' }, new byte[] { '4', '5' }, new byte[] { '6' }, new byte[] { '7', '8' }, new byte[] { '9', '0' }); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("12345", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("67890", byteChannel.dump(StandardCharsets.US_ASCII)); } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestPathAsyncEntityProducer.java0100664 0000000 0000000 00000010317 14403631147 031215 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.WritableByteChannelMock; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.BasicDataStreamChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestPathAsyncEntityProducer { private File tempFile; @AfterEach public void cleanup() { if (tempFile != null) { tempFile.delete(); tempFile = null; } } @BeforeEach public void setup() throws Exception { tempFile = File.createTempFile("testing", ".txt"); try (final Writer writer = new OutputStreamWriter(new FileOutputStream(tempFile), StandardCharsets.US_ASCII)) { writer.append("abcdef"); writer.flush(); } } @Test public void testTextContent() throws Exception { final Path tempPath = tempFile.toPath(); final AsyncEntityProducer producer = new PathEntityProducer(tempPath, ContentType.TEXT_PLAIN, StandardOpenOption.READ); Assertions.assertEquals(6, producer.getContentLength()); Assertions.assertEquals(ContentType.TEXT_PLAIN.toString(), producer.getContentType()); Assertions.assertNull(producer.getContentEncoding()); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("abcdef", byteChannel.dump(StandardCharsets.US_ASCII)); } @Test public void testTextContentRepeatable() throws Exception { final Path tempPath = tempFile.toPath(); final AsyncEntityProducer producer = new PathEntityProducer(tempPath, ContentType.TEXT_PLAIN, StandardOpenOption.READ); Assertions.assertEquals(6, producer.getContentLength()); Assertions.assertEquals(ContentType.TEXT_PLAIN.toString(), producer.getContentType()); Assertions.assertNull(producer.getContentEncoding()); for (int i = 0; i < 3; i++) { final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("abcdef", byteChannel.dump(StandardCharsets.US_ASCII)); producer.releaseResources(); } } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestDigestingEntityProducer.java0100664 0000000 0000000 00000005225 14403631147 031242 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.nio.charset.StandardCharsets; import java.util.List; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.WritableByteChannelMock; import org.apache.hc.core5.http.nio.BasicDataStreamChannel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDigestingEntityProducer { @Test public void testProduceData() throws Exception { final DigestingEntityProducer producer = new DigestingEntityProducer("MD5", new StringAsyncEntityProducer("12345", ContentType.TEXT_PLAIN)); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final BasicDataStreamChannel dataStreamChannel = new BasicDataStreamChannel(byteChannel); while (byteChannel.isOpen()) { producer.produce(dataStreamChannel); } Assertions.assertEquals("12345", byteChannel.dump(StandardCharsets.US_ASCII)); final List
trailers = dataStreamChannel.getTrailers(); Assertions.assertNotNull(trailers); Assertions.assertEquals(2, trailers.size()); Assertions.assertEquals("digest-algo", trailers.get(0).getName()); Assertions.assertEquals("MD5", trailers.get(0).getValue()); Assertions.assertEquals("digest", trailers.get(1).getName()); Assertions.assertEquals("827ccb0eea8a706c4c34a16891f84e7b", trailers.get(1).getValue()); } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestBasicAsyncEntityProducer.java0100664 0000000 0000000 00000011373 14403631147 031345 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.WritableByteChannelMock; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.BasicDataStreamChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestBasicAsyncEntityProducer { @Test public void testBinaryContent() throws Exception { final AsyncEntityProducer producer = new BasicAsyncEntityProducer( new byte[] { 'a', 'b', 'c' }, ContentType.DEFAULT_BINARY); Assertions.assertEquals(3, producer.getContentLength()); Assertions.assertEquals(ContentType.DEFAULT_BINARY.toString(), producer.getContentType()); Assertions.assertNull(producer.getContentEncoding()); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("abc", byteChannel.dump(StandardCharsets.US_ASCII)); } @Test public void testTextContent() throws Exception { final AsyncEntityProducer producer = new BasicAsyncEntityProducer( "abc", ContentType.TEXT_PLAIN); Assertions.assertEquals(3, producer.getContentLength()); Assertions.assertEquals(ContentType.TEXT_PLAIN.toString(), producer.getContentType()); Assertions.assertNull(producer.getContentEncoding()); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("abc", byteChannel.dump(StandardCharsets.US_ASCII)); } @Test public void testTextContentRepeatable() throws Exception { final AsyncEntityProducer producer = new BasicAsyncEntityProducer( "abc", ContentType.TEXT_PLAIN); Assertions.assertEquals(3, producer.getContentLength()); Assertions.assertEquals(ContentType.TEXT_PLAIN.toString(), producer.getContentType()); Assertions.assertNull(producer.getContentEncoding()); for (int i = 0; i < 3; i++) { final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("abc", byteChannel.dump(StandardCharsets.US_ASCII)); producer.releaseResources(); } } @Test public void testTextContentRepeatableUTF() throws Exception { final String content = ""; final AsyncEntityProducer producer = new BasicAsyncEntityProducer(content, ContentType.TEXT_XML); for (int i = 0; i < 3; i++) { final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals(content, byteChannel.dump(StandardCharsets.UTF_8)); producer.releaseResources(); } } } ././@LongLink0100644 0000000 0000000 00000000145 14403631147 011636 Lustar 0000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestAbstractCharAsyncEntityConsumer.javahttpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestAbstractCharAsyncEntityConsumer.java0100664 0000000 0000000 00000007345 14403631147 032701 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.impl.BasicEntityDetails; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestAbstractCharAsyncEntityConsumer { static private class StringBuilderAsyncEntityConsumer extends AbstractCharAsyncEntityConsumer { private final StringBuilder buffer; public StringBuilderAsyncEntityConsumer() { super(); this.buffer = new StringBuilder(1024); } @Override protected void streamStart(final ContentType contentType) throws HttpException, IOException { } @Override protected int capacityIncrement() { return Integer.MAX_VALUE; } @Override protected void data(final CharBuffer src, final boolean endOfStream) throws IOException { buffer.append(src); } @Override protected String generateContent() throws IOException { return buffer.toString(); } @Override public void releaseResources() { buffer.setLength(0); } } @Test public void testConsumeData() throws Exception { final AsyncEntityConsumer consumer = new StringBuilderAsyncEntityConsumer(); final AtomicLong count = new AtomicLong(0); consumer.streamStart(new BasicEntityDetails(-1, ContentType.TEXT_PLAIN), new FutureCallback() { @Override public void completed(final String result) { count.incrementAndGet(); } @Override public void failed(final Exception ex) { count.incrementAndGet(); } @Override public void cancelled() { count.incrementAndGet(); } }); consumer.consume(ByteBuffer.wrap(new byte[]{'1', '2', '3'})); consumer.consume(ByteBuffer.wrap(new byte[]{'4', '5'})); consumer.consume(ByteBuffer.wrap(new byte[]{})); Assertions.assertNull(consumer.getContent()); consumer.streamEnd(null); Assertions.assertEquals("12345", consumer.getContent()); Assertions.assertEquals(1L, count.longValue()); } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestAsyncEntityProducers.java0100664 0000000 0000000 00000006241 14403631147 030564 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.IOException; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests {@link AsyncEntityProducers}. */ public class TestAsyncEntityProducers { @Test public void testPathEntityProducer() throws IOException { final Path path = Paths.get("src/test/resources/test-ssl.txt"); final AsyncEntityProducer producer = AsyncEntityProducers.create(path, ContentType.APPLICATION_OCTET_STREAM, StandardOpenOption.READ, LinkOption.NOFOLLOW_LINKS); try { Assertions.assertFalse(producer.isChunked()); Assertions.assertEquals(Files.size(path), producer.getContentLength()); Assertions.assertEquals(ContentType.APPLICATION_OCTET_STREAM.toString(), producer.getContentType()); } finally { producer.releaseResources(); } } @Test public void testPathEntityProducerWithTrailers() throws IOException { final Path path = Paths.get("src/test/resources/test-ssl.txt"); final Header header1 = new BasicHeader("Tailer1", "Value1"); final Header header2 = new BasicHeader("Tailer2", "Value2"); final AsyncEntityProducer producer = AsyncEntityProducers.create(path, ContentType.APPLICATION_OCTET_STREAM, header1, header2); try { Assertions.assertTrue(producer.isChunked()); Assertions.assertEquals(-1, producer.getContentLength()); Assertions.assertEquals(ContentType.APPLICATION_OCTET_STREAM.toString(), producer.getContentType()); } finally { producer.releaseResources(); } } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestFileAsyncEntityProducer.java0100664 0000000 0000000 00000007766 14403631147 031216 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.WritableByteChannelMock; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.BasicDataStreamChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestFileAsyncEntityProducer { private File tempFile; @BeforeEach public void setup() throws Exception { tempFile = File.createTempFile("testing", ".txt"); try (final Writer writer = new OutputStreamWriter(new FileOutputStream(tempFile), StandardCharsets.US_ASCII)) { writer.append("abcdef"); writer.flush(); } } @AfterEach public void cleanup() { if (tempFile != null) { tempFile.delete(); tempFile = null; } } @Test public void testTextContent() throws Exception { final AsyncEntityProducer producer = new FileEntityProducer(tempFile, ContentType.TEXT_PLAIN); Assertions.assertEquals(6, producer.getContentLength()); Assertions.assertEquals(ContentType.TEXT_PLAIN.toString(), producer.getContentType()); Assertions.assertNull(producer.getContentEncoding()); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("abcdef", byteChannel.dump(StandardCharsets.US_ASCII)); } @Test public void testTextContentRepeatable() throws Exception { final AsyncEntityProducer producer = new FileEntityProducer(tempFile, ContentType.TEXT_PLAIN); Assertions.assertEquals(6, producer.getContentLength()); Assertions.assertEquals(ContentType.TEXT_PLAIN.toString(), producer.getContentType()); Assertions.assertNull(producer.getContentEncoding()); for (int i = 0; i < 3; i++) { final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("abcdef", byteChannel.dump(StandardCharsets.US_ASCII)); producer.releaseResources(); } } } httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestStringAsyncEntityProducer.java0100664 0000000 0000000 00000006424 14403631147 031573 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.nio.entity; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.WritableByteChannelMock; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.BasicDataStreamChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestStringAsyncEntityProducer { @Test public void testTextContent() throws Exception { final AsyncEntityProducer producer = new StringAsyncEntityProducer( "abc", ContentType.TEXT_PLAIN); Assertions.assertEquals(-1, producer.getContentLength()); Assertions.assertEquals(ContentType.TEXT_PLAIN.toString(), producer.getContentType()); Assertions.assertNull(producer.getContentEncoding()); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("abc", byteChannel.dump(StandardCharsets.US_ASCII)); } @Test public void testTextContentRepeatable() throws Exception { final AsyncEntityProducer producer = new StringAsyncEntityProducer( "abc", ContentType.TEXT_PLAIN); Assertions.assertEquals(-1, producer.getContentLength()); Assertions.assertEquals(ContentType.TEXT_PLAIN.toString(), producer.getContentType()); Assertions.assertNull(producer.getContentEncoding()); for (int i = 0; i < 3; i++) { final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("abc", byteChannel.dump(StandardCharsets.US_ASCII)); producer.releaseResources(); } } } httpcore5/src/test/java/org/apache/hc/core5/http/examples/0040775 0000000 0000000 00000000000 14435411677 022446 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/examples/ClassicFileServerExample.java0100664 0000000 0000000 00000017264 14403631147 030173 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import java.io.File; import java.io.IOException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.ExceptionListener; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.MethodNotSupportedException; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.FileEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.TimeValue; /** * Example of embedded HTTP/1.1 file server using classic I/O. */ public class ClassicFileServerExample { public static void main(final String[] args) throws Exception { if (args.length < 1) { System.err.println("Please specify document root directory"); System.exit(1); } // Document root directory final String docRoot = args[0]; int port = 8080; if (args.length >= 2) { port = Integer.parseInt(args[1]); } SSLContext sslContext = null; if (port == 8443) { // Initialize SSL context final URL url = ClassicFileServerExample.class.getResource("/my.keystore"); if (url == null) { System.out.println("Keystore not found"); System.exit(1); } sslContext = SSLContexts.custom() .loadKeyMaterial(url, "secret".toCharArray(), "secret".toCharArray()) .build(); } final SocketConfig socketConfig = SocketConfig.custom() .setSoTimeout(15, TimeUnit.SECONDS) .setTcpNoDelay(true) .build(); final HttpServer server = ServerBootstrap.bootstrap() .setListenerPort(port) .setSocketConfig(socketConfig) .setSslContext(sslContext) .setExceptionListener(new ExceptionListener() { @Override public void onError(final Exception ex) { ex.printStackTrace(); } @Override public void onError(final HttpConnection conn, final Exception ex) { if (ex instanceof SocketTimeoutException) { System.err.println("Connection timed out"); } else if (ex instanceof ConnectionClosedException) { System.err.println(ex.getMessage()); } else { ex.printStackTrace(); } } }) .register("*", new HttpFileHandler(docRoot)) .create(); server.start(); Runtime.getRuntime().addShutdownHook(new Thread(() -> server.close(CloseMode.GRACEFUL))); System.out.println("Listening on port " + port); server.awaitTermination(TimeValue.MAX_VALUE); } static class HttpFileHandler implements HttpRequestHandler { private final String docRoot; public HttpFileHandler(final String docRoot) { super(); this.docRoot = docRoot; } @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { final String method = request.getMethod(); if (!Method.GET.isSame(method) && !Method.HEAD.isSame(method) && !Method.POST.isSame(method)) { throw new MethodNotSupportedException(method + " method not supported"); } final String path = request.getPath(); final HttpEntity incomingEntity = request.getEntity(); if (incomingEntity != null) { final byte[] entityContent = EntityUtils.toByteArray(incomingEntity); System.out.println("Incoming incomingEntity content (bytes): " + entityContent.length); } final File file = new File(this.docRoot, URLDecoder.decode(path, "UTF-8")); if (!file.exists()) { response.setCode(HttpStatus.SC_NOT_FOUND); final String msg = "File " + file.getPath() + " not found"; final StringEntity outgoingEntity = new StringEntity( "

" + msg + "

", ContentType.create("text/html", "UTF-8")); response.setEntity(outgoingEntity); System.out.println(msg); } else if (!file.canRead() || file.isDirectory()) { response.setCode(HttpStatus.SC_FORBIDDEN); final String msg = "Cannot read file " + file.getPath(); final StringEntity outgoingEntity = new StringEntity( "

" + msg + "

", ContentType.create("text/html", "UTF-8")); response.setEntity(outgoingEntity); System.out.println(msg); } else { final HttpCoreContext coreContext = HttpCoreContext.adapt(context); final EndpointDetails endpoint = coreContext.getEndpointDetails(); response.setCode(HttpStatus.SC_OK); final FileEntity body = new FileEntity(file, ContentType.create("text/html", (Charset) null)); response.setEntity(body); System.out.println(endpoint + ": serving file " + file.getPath()); } } } } httpcore5/src/test/java/org/apache/hc/core5/http/examples/AsyncFullDuplexServerExample.java0100664 0000000 0000000 00000022420 14403631147 031062 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.util.TimeValue; /** * Example of full-duplex, streaming HTTP message exchanges with an asynchronous embedded HTTP/1.1 server. */ public class AsyncFullDuplexServerExample { public static void main(final String[] args) throws Exception { int port = 8080; if (args.length >= 1) { port = Integer.parseInt(args[0]); } final IOReactorConfig config = IOReactorConfig.custom() .setSoTimeout(15, TimeUnit.SECONDS) .setTcpNoDelay(true) .build(); final HttpAsyncServer server = AsyncServerBootstrap.bootstrap() .setExceptionCallback(e -> e.printStackTrace()) .setIOReactorConfig(config) .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { System.out.println(connection.getRemoteAddress() + " " + new RequestLine(request)); } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { System.out.println(connection.getRemoteAddress() + " " + new StatusLine(response)); } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (keepAlive) { System.out.println(connection.getRemoteAddress() + " exchange completed (connection kept alive)"); } else { System.out.println(connection.getRemoteAddress() + " exchange completed (connection closed)"); } } }) .register("/echo", () -> new AsyncServerExchangeHandler() { ByteBuffer buffer = ByteBuffer.allocate(2048); CapacityChannel inputCapacityChannel; DataStreamChannel outputDataChannel; boolean endStream; private void ensureCapacity(final int chunk) { if (buffer.remaining() < chunk) { final ByteBuffer oldBuffer = buffer; oldBuffer.flip(); buffer = ByteBuffer.allocate(oldBuffer.remaining() + (chunk > 2048 ? chunk : 2048)); buffer.put(oldBuffer); } } @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); responseChannel.sendResponse(response, entityDetails, context); } @Override public void consume(final ByteBuffer src) throws IOException { if (buffer.position() == 0) { if (outputDataChannel != null) { outputDataChannel.write(src); } } if (src.hasRemaining()) { ensureCapacity(src.remaining()); buffer.put(src); if (outputDataChannel != null) { outputDataChannel.requestOutput(); } } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { if (buffer.hasRemaining()) { capacityChannel.update(buffer.remaining()); inputCapacityChannel = null; } else { inputCapacityChannel = capacityChannel; } } @Override public void streamEnd(final List trailers) throws IOException { endStream = true; if (buffer.position() == 0) { if (outputDataChannel != null) { outputDataChannel.endStream(); } } else { if (outputDataChannel != null) { outputDataChannel.requestOutput(); } } } @Override public int available() { return buffer.position(); } @Override public void produce(final DataStreamChannel channel) throws IOException { outputDataChannel = channel; buffer.flip(); if (buffer.hasRemaining()) { channel.write(buffer); } buffer.compact(); if (buffer.position() == 0 && endStream) { channel.endStream(); } final CapacityChannel capacityChannel = inputCapacityChannel; if (capacityChannel != null && buffer.hasRemaining()) { capacityChannel.update(buffer.remaining()); } } @Override public void failed(final Exception cause) { if (!(cause instanceof SocketException)) { cause.printStackTrace(System.out); } } @Override public void releaseResources() { } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP server shutting down"); server.close(CloseMode.GRACEFUL); })); server.start(); final Future future = server.listen(new InetSocketAddress(port), URIScheme.HTTP); final ListenerEndpoint listenerEndpoint = future.get(); System.out.print("Listening on " + listenerEndpoint.getAddress()); server.awaitShutdown(TimeValue.MAX_VALUE); } } ././@LongLink0100644 0000000 0000000 00000000145 14403631147 011636 Lustar 0000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/examples/AsyncPipelinedRequestExecutionExample.javahttpcore5/src/test/java/org/apache/hc/core5/http/examples/AsyncPipelinedRequestExecutionExample.java0100664 0000000 0000000 00000014174 14403631147 032764 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.bootstrap.AsyncRequesterBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.Timeout; /** * Example of asynchronous HTTP/1.1 request execution. */ public class AsyncPipelinedRequestExecutionExample { public static void main(final String[] args) throws Exception { final IOReactorConfig ioReactorConfig = IOReactorConfig.custom() .setSoTimeout(5, TimeUnit.SECONDS) .build(); // Create and start requester final HttpAsyncRequester requester = AsyncRequesterBootstrap.bootstrap() .setIOReactorConfig(ioReactorConfig) .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { System.out.println(connection.getRemoteAddress() + " " + new RequestLine(request)); } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { System.out.println(connection.getRemoteAddress() + " " + new StatusLine(response)); } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (keepAlive) { System.out.println(connection.getRemoteAddress() + " exchange completed (connection kept alive)"); } else { System.out.println(connection.getRemoteAddress() + " exchange completed (connection closed)"); } } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP requester shutting down"); requester.close(CloseMode.GRACEFUL); })); requester.start(); final HttpHost target = new HttpHost("httpbin.org"); final String[] requestUris = new String[] {"/", "/ip", "/user-agent", "/headers"}; final Future future = requester.connect(target, Timeout.ofSeconds(5)); final AsyncClientEndpoint clientEndpoint = future.get(); final CountDownLatch latch = new CountDownLatch(requestUris.length); for (final String requestUri: requestUris) { clientEndpoint.execute( AsyncRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), new FutureCallback>() { @Override public void completed(final Message message) { latch.countDown(); final HttpResponse response = message.getHead(); final String body = message.getBody(); System.out.println(requestUri + "->" + response.getCode()); System.out.println(body); } @Override public void failed(final Exception ex) { latch.countDown(); System.out.println(requestUri + "->" + ex); } @Override public void cancelled() { latch.countDown(); System.out.println(requestUri + " cancelled"); } }); } latch.await(); // Manually release client endpoint when done !!! clientEndpoint.releaseAndDiscard(); System.out.println("Shutting down I/O reactor"); requester.initiateShutdown(); } } httpcore5/src/test/java/org/apache/hc/core5/http/examples/ClassicGetExecutionExample.java0100664 0000000 0000000 00000010730 14315123013 030505 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.bootstrap.RequesterBootstrap; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.util.Timeout; /** * Example of GET requests execution using classic I/O. */ public class ClassicGetExecutionExample { public static void main(final String[] args) throws Exception { final HttpRequester httpRequester = RequesterBootstrap.bootstrap() .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { System.out.println(connection.getRemoteAddress() + " " + new RequestLine(request)); } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { System.out.println(connection.getRemoteAddress() + " " + new StatusLine(response)); } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (keepAlive) { System.out.println(connection.getRemoteAddress() + " exchange completed (connection kept alive)"); } else { System.out.println(connection.getRemoteAddress() + " exchange completed (connection closed)"); } } }) .setSocketConfig(SocketConfig.custom() .setSoTimeout(5, TimeUnit.SECONDS) .build()) .create(); final HttpCoreContext coreContext = HttpCoreContext.create(); final HttpHost target = new HttpHost("httpbin.org"); final String[] requestUris = new String[] {"/", "/ip", "/user-agent", "/headers"}; for (int i = 0; i < requestUris.length; i++) { final String requestUri = requestUris[i]; final ClassicHttpRequest request = ClassicRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(); try (ClassicHttpResponse response = httpRequester.execute(target, request, Timeout.ofSeconds(5), coreContext)) { System.out.println(requestUri + "->" + response.getCode()); System.out.println(EntityUtils.toString(response.getEntity())); System.out.println("=============="); } } } } httpcore5/src/test/java/org/apache/hc/core5/http/examples/AsyncFullDuplexClientExample.java0100664 0000000 0000000 00000017500 14403631147 031035 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.bootstrap.AsyncRequesterBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.Timeout; /** * Example of full-duplex, streaming HTTP message exchanges with an asynchronous HTTP/1.1 requester. */ public class AsyncFullDuplexClientExample { public static void main(final String[] args) throws Exception { final IOReactorConfig ioReactorConfig = IOReactorConfig.custom() .setSoTimeout(5, TimeUnit.SECONDS) .build(); // Create and start requester // Disable 'Expect: Continue' handshake some servers cannot handle well final HttpAsyncRequester requester = AsyncRequesterBootstrap.bootstrap() .setIOReactorConfig(ioReactorConfig) .setHttpProcessor(HttpProcessors.customClient(null).addLast((HttpRequestInterceptor) (request, entity, context) -> request.removeHeaders(HttpHeaders.EXPECT)).build()) .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { System.out.println(connection.getRemoteAddress() + " " + new RequestLine(request)); } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { System.out.println(connection.getRemoteAddress() + " " + new StatusLine(response)); } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (keepAlive) { System.out.println(connection.getRemoteAddress() + " exchange completed (connection kept alive)"); } else { System.out.println(connection.getRemoteAddress() + " exchange completed (connection closed)"); } } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP requester shutting down"); requester.close(CloseMode.GRACEFUL); })); requester.start(); final URI requestUri = new URI("http://httpbin.org/post"); final AsyncRequestProducer requestProducer = AsyncRequestBuilder.post(requestUri) .setEntity("stuff") .build(); final BasicResponseConsumer responseConsumer = new BasicResponseConsumer<>( new StringAsyncEntityConsumer()); final CountDownLatch latch = new CountDownLatch(1); requester.execute(new AsyncClientExchangeHandler() { @Override public void releaseResources() { requestProducer.releaseResources(); responseConsumer.releaseResources(); latch.countDown(); } @Override public void cancel() { System.out.println(requestUri + " cancelled"); } @Override public void failed(final Exception cause) { System.out.println(requestUri + "->" + cause); } @Override public void produceRequest(final RequestChannel channel, final HttpContext httpContext) throws HttpException, IOException { requestProducer.sendRequest(channel, httpContext); } @Override public int available() { return requestProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { requestProducer.produce(channel); } @Override public void consumeInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { System.out.println(requestUri + "->" + response.getCode()); } @Override public void consumeResponse(final HttpResponse response, final EntityDetails entityDetails, final HttpContext httpContext) throws HttpException, IOException { System.out.println(requestUri + "->" + response.getCode()); responseConsumer.consumeResponse(response, entityDetails, httpContext, null); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { responseConsumer.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { responseConsumer.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { responseConsumer.streamEnd(trailers); } }, Timeout.ofSeconds(30), HttpCoreContext.create()); latch.await(1, TimeUnit.MINUTES); System.out.println("Shutting down I/O reactor"); requester.initiateShutdown(); } } httpcore5/src/test/java/org/apache/hc/core5/http/examples/ClassicPostExecutionExample.java0100664 0000000 0000000 00000013147 14435411677 030743 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.bootstrap.RequesterBootstrap; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.HttpEntities; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.util.Timeout; /** * Example of POST requests execution using classic I/O. */ public class ClassicPostExecutionExample { public static void main(final String[] args) throws Exception { final HttpRequester httpRequester = RequesterBootstrap.bootstrap() .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { System.out.println(connection.getRemoteAddress() + " " + new RequestLine(request)); } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { System.out.println(connection.getRemoteAddress() + " " + new StatusLine(response)); } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (keepAlive) { System.out.println(connection.getRemoteAddress() + " exchange completed (connection kept alive)"); } else { System.out.println(connection.getRemoteAddress() + " exchange completed (connection closed)"); } } }) .setSocketConfig(SocketConfig.custom() .setSoTimeout(5, TimeUnit.SECONDS) .build()) .create(); final HttpCoreContext coreContext = HttpCoreContext.create(); final HttpHost target = new HttpHost("httpbin.org"); final HttpEntity[] requestBodies = { HttpEntities.create( "This is the first test request", ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8)), HttpEntities.create( "This is the second test request".getBytes(StandardCharsets.UTF_8), ContentType.APPLICATION_OCTET_STREAM), HttpEntities.create(outStream -> outStream.write(("This is the third test request " + "(streaming)").getBytes(StandardCharsets.UTF_8)), ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8)), HttpEntities.create( "This is the fourth test request " + "(streaming with trailers)", ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8), new BasicHeader("trailer1","And goodbye")) }; final String requestUri = "/post"; for (int i = 0; i < requestBodies.length; i++) { final ClassicHttpRequest request = ClassicRequestBuilder.post() .setHttpHost(target) .setPath(requestUri) .build(); request.setEntity(requestBodies[i]); try (ClassicHttpResponse response = httpRequester.execute(target, request, Timeout.ofSeconds(5), coreContext)) { System.out.println(requestUri + "->" + response.getCode()); System.out.println(EntityUtils.toString(response.getEntity())); System.out.println("=============="); } } } } httpcore5/src/test/java/org/apache/hc/core5/http/examples/ClassicServerFilterExample.java0100664 0000000 0000000 00000013565 14403631147 030541 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap; import org.apache.hc.core5.http.impl.bootstrap.StandardFilter; import org.apache.hc.core5.http.io.HttpFilterChain; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.io.support.AbstractHttpServerAuthFilter; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.TimeValue; /** * Example of using classic I/O request filters with an embedded HTTP/1.1 server. */ public class ClassicServerFilterExample { public static void main(final String[] args) throws Exception { int port = 8080; if (args.length >= 1) { port = Integer.parseInt(args[0]); } final SocketConfig socketConfig = SocketConfig.custom() .setSoTimeout(15, TimeUnit.SECONDS) .setTcpNoDelay(true) .build(); final HttpServer server = ServerBootstrap.bootstrap() .setListenerPort(port) .setSocketConfig(socketConfig) // Replace standard expect-continue handling with a custom auth filter .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), new AbstractHttpServerAuthFilter(false) { @Override protected String parseChallengeResponse( final String authorizationValue, final HttpContext context) throws HttpException { return authorizationValue; } @Override protected boolean authenticate( final String challengeResponse, final URIAuthority authority, final String requestUri, final HttpContext context) { return "let me pass".equals(challengeResponse); } @Override protected String generateChallenge( final String challengeResponse, final URIAuthority authority, final String requestUri, final HttpContext context) { return "who goes there?"; } }) // Add a custom request filter at the beginning of the processing pipeline .addFilterFirst("my-filter", (request, responseTrigger, context, chain) -> { if (request.getRequestUri().equals("/back-door")) { final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK); response.setEntity(new StringEntity("Welcome", ContentType.TEXT_PLAIN)); responseTrigger.submitResponse(response); } else { chain.proceed(request, new HttpFilterChain.ResponseTrigger() { @Override public void sendInformation(final ClassicHttpResponse response) throws HttpException, IOException { responseTrigger.sendInformation(response); } @Override public void submitResponse(final ClassicHttpResponse response) throws HttpException, IOException { response.addHeader("X-Filter", "My-Filter"); responseTrigger.submitResponse(response); } }, context); } }) // Application request handler .register("*", (request, response, context) -> { // do something useful response.setCode(HttpStatus.SC_OK); response.setEntity(new StringEntity("Hello")); }) .create(); server.start(); Runtime.getRuntime().addShutdownHook(new Thread(() -> server.close(CloseMode.GRACEFUL))); System.out.println("Listening on port " + port); server.awaitTermination(TimeValue.MAX_VALUE); } } httpcore5/src/test/java/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java0100664 0000000 0000000 00000074502 14403631147 030274 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import java.io.IOException; import java.io.InterruptedIOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.BasicEntityDetails; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.bootstrap.AsyncRequesterBootstrap; import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.impl.nio.BufferedData; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpDateGenerator; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.pool.ConnPoolListener; import org.apache.hc.core5.pool.ConnPoolStats; import org.apache.hc.core5.pool.PoolStats; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Example of asynchronous embedded HTTP/1.1 reverse proxy with full content streaming. */ public class AsyncReverseProxyExample { private static boolean quiet; public static void main(final String[] args) throws Exception { if (args.length < 1) { System.out.println("Usage: [listener port] [--quiet]"); System.exit(1); } // Target host final HttpHost targetHost = HttpHost.create(args[0]); int port = 8080; if (args.length > 1) { port = Integer.parseInt(args[1]); } for (final String s : args) { if ("--quiet".equalsIgnoreCase(s)) { quiet = true; break; } } println("Reverse proxy to " + targetHost); final IOReactorConfig config = IOReactorConfig.custom() .setSoTimeout(1, TimeUnit.MINUTES) .build(); final HttpAsyncRequester requester = AsyncRequesterBootstrap.bootstrap() .setIOReactorConfig(config) .setConnPoolListener(new ConnPoolListener() { @Override public void onLease(final HttpHost route, final ConnPoolStats connPoolStats) { final StringBuilder buf = new StringBuilder(); buf.append("[proxy->origin] connection leased ").append(route); println(buf.toString()); } @Override public void onRelease(final HttpHost route, final ConnPoolStats connPoolStats) { final StringBuilder buf = new StringBuilder(); buf.append("[proxy->origin] connection released ").append(route); final PoolStats totals = connPoolStats.getTotalStats(); buf.append("; total kept alive: ").append(totals.getAvailable()).append("; "); buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable()); buf.append(" of ").append(totals.getMax()); println(buf.toString()); } }) .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { // empty } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { // empty } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { println("[proxy<-origin] connection " + connection.getLocalAddress() + "->" + connection.getRemoteAddress() + (keepAlive ? " kept alive" : " cannot be kept alive")); } }) .setMaxTotal(100) .setDefaultMaxPerRoute(20) .create(); final HttpAsyncServer server = AsyncServerBootstrap.bootstrap() .setExceptionCallback(e -> e.printStackTrace()) .setIOReactorConfig(config) .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { // empty } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { // empty } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { println("[client<-proxy] connection " + connection.getLocalAddress() + "->" + connection.getRemoteAddress() + (keepAlive ? " kept alive" : " cannot be kept alive")); } }) .register("*", () -> new IncomingExchangeHandler(targetHost, requester)) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { println("Reverse proxy shutting down"); server.close(CloseMode.GRACEFUL); requester.close(CloseMode.GRACEFUL); })); requester.start(); server.start(); server.listen(new InetSocketAddress(port), URIScheme.HTTP); println("Listening on port " + port); server.awaitShutdown(TimeValue.MAX_VALUE); } private static class ProxyBuffer extends BufferedData { ProxyBuffer(final int bufferSize) { super(bufferSize); } int write(final DataStreamChannel channel) throws IOException { setOutputMode(); if (buffer().hasRemaining()) { return channel.write(buffer()); } return 0; } } private static final AtomicLong COUNT = new AtomicLong(0); private static class ProxyExchangeState { final String id; HttpRequest request; EntityDetails requestEntityDetails; DataStreamChannel requestDataChannel; CapacityChannel requestCapacityChannel; ProxyBuffer inBuf; boolean inputEnd; HttpResponse response; EntityDetails responseEntityDetails; ResponseChannel responseMessageChannel; DataStreamChannel responseDataChannel; CapacityChannel responseCapacityChannel; ProxyBuffer outBuf; boolean outputEnd; AsyncClientEndpoint clientEndpoint; ProxyExchangeState() { this.id = String.format("%010d", COUNT.getAndIncrement()); } } private static final int INIT_BUFFER_SIZE = 4096; private static class IncomingExchangeHandler implements AsyncServerExchangeHandler { private final HttpHost targetHost; private final HttpAsyncRequester requester; private final ProxyExchangeState exchangeState; IncomingExchangeHandler(final HttpHost targetHost, final HttpAsyncRequester requester) { super(); this.targetHost = targetHost; this.requester = requester; this.exchangeState = new ProxyExchangeState(); } @Override public void handleRequest( final HttpRequest incomingRequest, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext httpContext) throws HttpException, IOException { synchronized (exchangeState) { println("[client->proxy] " + exchangeState.id + " " + incomingRequest.getMethod() + " " + incomingRequest.getRequestUri()); exchangeState.request = incomingRequest; exchangeState.requestEntityDetails = entityDetails; exchangeState.inputEnd = entityDetails == null; exchangeState.responseMessageChannel = responseChannel; if (entityDetails != null) { final Header h = incomingRequest.getFirstHeader(HttpHeaders.EXPECT); if (h != null && HeaderElements.CONTINUE.equalsIgnoreCase(h.getValue())) { responseChannel.sendInformation(new BasicHttpResponse(HttpStatus.SC_CONTINUE), httpContext); } } } println("[proxy->origin] " + exchangeState.id + " request connection to " + targetHost); requester.connect(targetHost, Timeout.ofSeconds(30), null, new FutureCallback() { @Override public void completed(final AsyncClientEndpoint clientEndpoint) { println("[proxy->origin] " + exchangeState.id + " connection leased"); synchronized (exchangeState) { exchangeState.clientEndpoint = clientEndpoint; } clientEndpoint.execute( new OutgoingExchangeHandler(targetHost, clientEndpoint, exchangeState), HttpCoreContext.create()); } @Override public void failed(final Exception cause) { final HttpResponse outgoingResponse = new BasicHttpResponse(HttpStatus.SC_SERVICE_UNAVAILABLE); outgoingResponse.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); final ByteBuffer msg = StandardCharsets.US_ASCII.encode(CharBuffer.wrap(cause.getMessage())); final EntityDetails exEntityDetails = new BasicEntityDetails(msg.remaining(), ContentType.TEXT_PLAIN); synchronized (exchangeState) { exchangeState.response = outgoingResponse; exchangeState.responseEntityDetails = exEntityDetails; exchangeState.outBuf = new ProxyBuffer(1024); exchangeState.outBuf.put(msg); exchangeState.outputEnd = true; } println("[client<-proxy] " + exchangeState.id + " status " + outgoingResponse.getCode()); try { responseChannel.sendResponse(outgoingResponse, exEntityDetails, httpContext); } catch (final HttpException | IOException ignore) { // ignore } } @Override public void cancelled() { failed(new InterruptedIOException()); } }); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { synchronized (exchangeState) { exchangeState.requestCapacityChannel = capacityChannel; final int capacity = exchangeState.inBuf != null ? exchangeState.inBuf.capacity() : INIT_BUFFER_SIZE; if (capacity > 0) { println("[client<-proxy] " + exchangeState.id + " input capacity: " + capacity); capacityChannel.update(capacity); } } } @Override public void consume(final ByteBuffer src) throws IOException { synchronized (exchangeState) { println("[client->proxy] " + exchangeState.id + " " + src.remaining() + " bytes received"); final DataStreamChannel dataChannel = exchangeState.requestDataChannel; if (dataChannel != null && exchangeState.inBuf != null) { if (exchangeState.inBuf.hasData()) { final int bytesWritten = exchangeState.inBuf.write(dataChannel); println("[proxy->origin] " + exchangeState.id + " " + bytesWritten + " bytes sent"); } if (!exchangeState.inBuf.hasData()) { final int bytesWritten = dataChannel.write(src); println("[proxy->origin] " + exchangeState.id + " " + bytesWritten + " bytes sent"); } } if (src.hasRemaining()) { if (exchangeState.inBuf == null) { exchangeState.inBuf = new ProxyBuffer(INIT_BUFFER_SIZE); } exchangeState.inBuf.put(src); } final int capacity = exchangeState.inBuf != null ? exchangeState.inBuf.capacity() : INIT_BUFFER_SIZE; println("[client<-proxy] " + exchangeState.id + " input capacity: " + capacity); if (dataChannel != null) { dataChannel.requestOutput(); } } } @Override public void streamEnd(final List trailers) throws HttpException, IOException { synchronized (exchangeState) { println("[client->proxy] " + exchangeState.id + " end of input"); exchangeState.inputEnd = true; final DataStreamChannel dataChannel = exchangeState.requestDataChannel; if (dataChannel != null && (exchangeState.inBuf == null || !exchangeState.inBuf.hasData())) { println("[proxy->origin] " + exchangeState.id + " end of output"); dataChannel.endStream(); } } } @Override public int available() { synchronized (exchangeState) { final int available = exchangeState.outBuf != null ? exchangeState.outBuf.length() : 0; println("[client<-proxy] " + exchangeState.id + " output available: " + available); return available; } } @Override public void produce(final DataStreamChannel channel) throws IOException { synchronized (exchangeState) { println("[client<-proxy] " + exchangeState.id + " produce output"); exchangeState.responseDataChannel = channel; if (exchangeState.outBuf != null) { if (exchangeState.outBuf.hasData()) { final int bytesWritten = exchangeState.outBuf.write(channel); println("[client<-proxy] " + exchangeState.id + " " + bytesWritten + " bytes sent"); } if (exchangeState.outputEnd && !exchangeState.outBuf.hasData()) { channel.endStream(); println("[client<-proxy] " + exchangeState.id + " end of output"); } if (!exchangeState.outputEnd) { final CapacityChannel capacityChannel = exchangeState.responseCapacityChannel; if (capacityChannel != null) { final int capacity = exchangeState.outBuf.capacity(); if (capacity > 0) { println("[proxy->origin] " + exchangeState.id + " input capacity: " + capacity); capacityChannel.update(capacity); } } } } } } @Override public void failed(final Exception cause) { println("[client<-proxy] " + exchangeState.id + " " + cause.getMessage()); if (!(cause instanceof ConnectionClosedException)) { cause.printStackTrace(System.out); } synchronized (exchangeState) { if (exchangeState.clientEndpoint != null) { exchangeState.clientEndpoint.releaseAndDiscard(); } } } @Override public void releaseResources() { synchronized (exchangeState) { exchangeState.responseMessageChannel = null; exchangeState.responseDataChannel = null; exchangeState.requestCapacityChannel = null; } } } private static class OutgoingExchangeHandler implements AsyncClientExchangeHandler { private final static Set HOP_BY_HOP = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( TextUtils.toLowerCase(HttpHeaders.HOST), TextUtils.toLowerCase(HttpHeaders.CONTENT_LENGTH), TextUtils.toLowerCase(HttpHeaders.TRANSFER_ENCODING), TextUtils.toLowerCase(HttpHeaders.CONNECTION), TextUtils.toLowerCase(HttpHeaders.KEEP_ALIVE), TextUtils.toLowerCase(HttpHeaders.PROXY_AUTHENTICATE), TextUtils.toLowerCase(HttpHeaders.TE), TextUtils.toLowerCase(HttpHeaders.TRAILER), TextUtils.toLowerCase(HttpHeaders.UPGRADE)))); private final HttpHost targetHost; private final AsyncClientEndpoint clientEndpoint; private final ProxyExchangeState exchangeState; OutgoingExchangeHandler( final HttpHost targetHost, final AsyncClientEndpoint clientEndpoint, final ProxyExchangeState exchangeState) { this.targetHost = targetHost; this.clientEndpoint = clientEndpoint; this.exchangeState = exchangeState; } @Override public void produceRequest( final RequestChannel channel, final HttpContext httpContext) throws HttpException, IOException { synchronized (exchangeState) { final HttpRequest incomingRequest = exchangeState.request; final EntityDetails entityDetails = exchangeState.requestEntityDetails; final HttpRequest outgoingRequest = new BasicHttpRequest( incomingRequest.getMethod(), targetHost, incomingRequest.getPath()); for (final Iterator
it = incomingRequest.headerIterator(); it.hasNext(); ) { final Header header = it.next(); if (!HOP_BY_HOP.contains(TextUtils.toLowerCase(header.getName()))) { outgoingRequest.addHeader(header); } } println("[proxy->origin] " + exchangeState.id + " " + outgoingRequest.getMethod() + " " + outgoingRequest.getRequestUri()); channel.sendRequest(outgoingRequest, entityDetails, httpContext); } } @Override public int available() { synchronized (exchangeState) { final int available = exchangeState.inBuf != null ? exchangeState.inBuf.length() : 0; println("[proxy->origin] " + exchangeState.id + " output available: " + available); return available; } } @Override public void produce(final DataStreamChannel channel) throws IOException { synchronized (exchangeState) { println("[proxy->origin] " + exchangeState.id + " produce output"); exchangeState.requestDataChannel = channel; if (exchangeState.inBuf != null) { if (exchangeState.inBuf.hasData()) { final int bytesWritten = exchangeState.inBuf.write(channel); println("[proxy->origin] " + exchangeState.id + " " + bytesWritten + " bytes sent"); } if (exchangeState.inputEnd && !exchangeState.inBuf.hasData()) { channel.endStream(); println("[proxy->origin] " + exchangeState.id + " end of output"); } if (!exchangeState.inputEnd) { final CapacityChannel capacityChannel = exchangeState.requestCapacityChannel; if (capacityChannel != null) { final int capacity = exchangeState.inBuf.capacity(); if (capacity > 0) { println("[client<-proxy] " + exchangeState.id + " input capacity: " + capacity); capacityChannel.update(capacity); } } } } } } @Override public void consumeInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { // ignore } @Override public void consumeResponse( final HttpResponse incomingResponse, final EntityDetails entityDetails, final HttpContext httpContext) throws HttpException, IOException { synchronized (exchangeState) { println("[proxy<-origin] " + exchangeState.id + " status " + incomingResponse.getCode()); if (entityDetails == null) { println("[proxy<-origin] " + exchangeState.id + " end of input"); } final HttpResponse outgoingResponse = new BasicHttpResponse(incomingResponse.getCode()); for (final Iterator
it = incomingResponse.headerIterator(); it.hasNext(); ) { final Header header = it.next(); if (!HOP_BY_HOP.contains(TextUtils.toLowerCase(header.getName()))) { outgoingResponse.addHeader(header); } } exchangeState.response = outgoingResponse; exchangeState.responseEntityDetails = entityDetails; exchangeState.outputEnd = entityDetails == null; final ResponseChannel responseChannel = exchangeState.responseMessageChannel; if (responseChannel != null) { // responseChannel can be null under load. responseChannel.sendResponse(outgoingResponse, entityDetails, httpContext); } println("[client<-proxy] " + exchangeState.id + " status " + outgoingResponse.getCode()); if (entityDetails == null) { println("[client<-proxy] " + exchangeState.id + " end of output"); clientEndpoint.releaseAndReuse(); } } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { synchronized (exchangeState) { exchangeState.responseCapacityChannel = capacityChannel; final int capacity = exchangeState.outBuf != null ? exchangeState.outBuf.capacity() : INIT_BUFFER_SIZE; if (capacity > 0) { println("[proxy->origin] " + exchangeState.id + " input capacity: " + capacity); capacityChannel.update(capacity); } } } @Override public void consume(final ByteBuffer src) throws IOException { synchronized (exchangeState) { println("[proxy<-origin] " + exchangeState.id + " " + src.remaining() + " bytes received"); final DataStreamChannel dataChannel = exchangeState.responseDataChannel; if (dataChannel != null && exchangeState.outBuf != null) { if (exchangeState.outBuf.hasData()) { final int bytesWritten = exchangeState.outBuf.write(dataChannel); println("[client<-proxy] " + exchangeState.id + " " + bytesWritten + " bytes sent"); } if (!exchangeState.outBuf.hasData()) { final int bytesWritten = dataChannel.write(src); println("[client<-proxy] " + exchangeState.id + " " + bytesWritten + " bytes sent"); } } if (src.hasRemaining()) { if (exchangeState.outBuf == null) { exchangeState.outBuf = new ProxyBuffer(INIT_BUFFER_SIZE); } exchangeState.outBuf.put(src); } final int capacity = exchangeState.outBuf != null ? exchangeState.outBuf.capacity() : INIT_BUFFER_SIZE; println("[proxy->origin] " + exchangeState.id + " input capacity: " + capacity); if (dataChannel != null) { dataChannel.requestOutput(); } } } @Override public void streamEnd(final List trailers) throws HttpException, IOException { synchronized (exchangeState) { println("[proxy<-origin] " + exchangeState.id + " end of input"); exchangeState.outputEnd = true; final DataStreamChannel dataChannel = exchangeState.responseDataChannel; if (dataChannel != null && (exchangeState.outBuf == null || !exchangeState.outBuf.hasData())) { println("[client<-proxy] " + exchangeState.id + " end of output"); dataChannel.endStream(); clientEndpoint.releaseAndReuse(); } } } @Override public void cancel() { clientEndpoint.releaseAndDiscard(); } @Override public void failed(final Exception cause) { println("[client<-proxy] " + exchangeState.id + " " + cause.getMessage()); if (!(cause instanceof ConnectionClosedException)) { cause.printStackTrace(System.out); } synchronized (exchangeState) { if (exchangeState.response == null) { final int status = cause instanceof IOException ? HttpStatus.SC_SERVICE_UNAVAILABLE : HttpStatus.SC_INTERNAL_SERVER_ERROR; final HttpResponse outgoingResponse = new BasicHttpResponse(status); outgoingResponse.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); exchangeState.response = outgoingResponse; final ByteBuffer msg = StandardCharsets.US_ASCII.encode(CharBuffer.wrap(cause.getMessage())); final int contentLen = msg.remaining(); exchangeState.outBuf = new ProxyBuffer(1024); exchangeState.outBuf.put(msg); exchangeState.outputEnd = true; println("[client<-proxy] " + exchangeState.id + " status " + outgoingResponse.getCode()); try { final EntityDetails entityDetails = new BasicEntityDetails(contentLen, ContentType.TEXT_PLAIN); exchangeState.responseMessageChannel.sendResponse(outgoingResponse, entityDetails, null); } catch (final HttpException | IOException ignore) { // ignore } } else { exchangeState.outputEnd = true; } clientEndpoint.releaseAndDiscard(); } } @Override public void releaseResources() { synchronized (exchangeState) { exchangeState.requestDataChannel = null; exchangeState.responseCapacityChannel = null; clientEndpoint.releaseAndDiscard(); } } } static void println(final String msg) { if (!quiet) { System.out.println(HttpDateGenerator.INSTANCE.getCurrentDate() + " | " + msg); } } } httpcore5/src/test/java/org/apache/hc/core5/http/examples/PrintVersionInfo.java0100664 0000000 0000000 00000005450 14315123013 026545 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import org.apache.hc.core5.util.VersionInfo; /** * Prints version information for debugging purposes. * This can be used to verify that the correct versions of the * HttpComponent JARs are picked up from the classpath. * * */ public class PrintVersionInfo { /** A default list of module packages. */ private final static String[] MODULE_LIST = { "org.apache.http", // HttpCore "org.apache.http.client", // HttpClient }; /** * Prints version information. * * @param args command line arguments. Leave empty to print version * information for the default packages. Otherwise, pass * a list of packages for which to get version info. */ public static void main(final String args[]) { final String[] pckgs = (args.length > 0) ? args : MODULE_LIST; VersionInfo[] via = VersionInfo.loadVersionInfo(pckgs, null); System.out.println("version info for thread context classloader:"); for (int i=0; i. * */ package org.apache.hc.core5.http.examples; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.impl.bootstrap.StandardFilter; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncFilterChain; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AbstractAsyncServerAuthFilter; import org.apache.hc.core5.http.nio.support.AsyncResponseBuilder; import org.apache.hc.core5.http.nio.support.BasicRequestConsumer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.util.TimeValue; /** * Example of using asynchronous I/O request filters with an embedded HTTP/1.1 server. */ public class AsyncServerFilterExample { public static void main(final String[] args) throws Exception { int port = 8080; if (args.length >= 1) { port = Integer.parseInt(args[0]); } final IOReactorConfig config = IOReactorConfig.custom() .setSoTimeout(15, TimeUnit.SECONDS) .setTcpNoDelay(true) .build(); final HttpAsyncServer server = AsyncServerBootstrap.bootstrap() .setExceptionCallback(e -> e.printStackTrace()) .setIOReactorConfig(config) // Replace standard expect-continue handling with a custom auth filter .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), new AbstractAsyncServerAuthFilter(true) { @Override protected String parseChallengeResponse( final String authorizationValue, final HttpContext context) throws HttpException { return authorizationValue; } @Override protected boolean authenticate( final String challengeResponse, final URIAuthority authority, final String requestUri, final HttpContext context) { return "let me pass".equals(challengeResponse); } @Override protected String generateChallenge( final String challengeResponse, final URIAuthority authority, final String requestUri, final HttpContext context) { return "who goes there?"; } }) // Add a custom request filter at the beginning of the processing pipeline .addFilterFirst("my-filter", (request, entityDetails, context, responseTrigger, chain) -> { if (request.getRequestUri().equals("/back-door")) { responseTrigger.submitResponse( new BasicHttpResponse(HttpStatus.SC_OK), AsyncEntityProducers.create("Welcome")); return null; } return chain.proceed(request, entityDetails, context, new AsyncFilterChain.ResponseTrigger() { @Override public void sendInformation( final HttpResponse response) throws HttpException, IOException { responseTrigger.sendInformation(response); } @Override public void submitResponse( final HttpResponse response, final AsyncEntityProducer entityProducer) throws HttpException, IOException { response.addHeader("X-Filter", "My-Filter"); responseTrigger.submitResponse(response, entityProducer); } @Override public void pushPromise( final HttpRequest promise, final AsyncPushProducer responseProducer) throws HttpException, IOException { responseTrigger.pushPromise(promise, responseProducer); } }); }) // Application request handler .register("*", new AsyncServerRequestHandler>() { @Override public AsyncRequestConsumer> prepare( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) throws HttpException { return new BasicRequestConsumer<>(entityDetails != null ? new StringAsyncEntityConsumer() : null); } @Override public void handle( final Message requestMessage, final ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { // do something useful responseTrigger.submitResponse( AsyncResponseBuilder.create(HttpStatus.SC_OK) .setEntity("Hello") .build(), context); } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP server shutting down"); server.close(CloseMode.GRACEFUL); })); server.start(); final Future future = server.listen(new InetSocketAddress(port), URIScheme.HTTP); final ListenerEndpoint listenerEndpoint = future.get(); System.out.print("Listening on " + listenerEndpoint.getAddress()); server.awaitShutdown(TimeValue.MAX_VALUE); } } httpcore5/src/test/java/org/apache/hc/core5/http/examples/AsyncFileServerExample.java0100664 0000000 0000000 00000020151 14403631147 027654 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.nio.entity.DiscardingEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncResponseBuilder; import org.apache.hc.core5.http.nio.support.BasicRequestConsumer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpDateGenerator; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.TimeValue; /** * Example of asynchronous embedded HTTP/1.1 file server. */ public class AsyncFileServerExample { /** * Example command line args: {@code "c:\temp" 8080} */ public static void main(final String[] args) throws Exception { if (args.length < 1) { System.err.println("Please specify document root directory"); System.exit(1); } // Document root directory final File docRoot = new File(args[0]); int port = 8080; if (args.length >= 2) { port = Integer.parseInt(args[1]); } final IOReactorConfig config = IOReactorConfig.custom() .setSoTimeout(15, TimeUnit.SECONDS) .setTcpNoDelay(true) .build(); final HttpAsyncServer server = AsyncServerBootstrap.bootstrap() .setExceptionCallback(e -> e.printStackTrace()) .setIOReactorConfig(config) .register("*", new AsyncServerRequestHandler>() { @Override public AsyncRequestConsumer> prepare( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) throws HttpException { return new BasicRequestConsumer<>(entityDetails != null ? new DiscardingEntityConsumer<>() : null); } @Override public void handle( final Message message, final ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { final HttpRequest request = message.getHead(); final URI requestUri; try { requestUri = request.getUri(); } catch (final URISyntaxException ex) { throw new ProtocolException(ex.getMessage(), ex); } final String path = requestUri.getPath(); final File file = new File(docRoot, path); if (!file.exists()) { final String msg = "File " + file.getPath() + " not found"; println(msg); responseTrigger.submitResponse( AsyncResponseBuilder.create(HttpStatus.SC_NOT_FOUND) .setEntity("

" + msg + "

", ContentType.TEXT_HTML) .build(), context); } else if (!file.canRead() || file.isDirectory()) { final String msg = "Cannot read file " + file.getPath(); println(msg); responseTrigger.submitResponse(AsyncResponseBuilder.create(HttpStatus.SC_FORBIDDEN) .setEntity("

" + msg + "

", ContentType.TEXT_HTML) .build(), context); } else { final ContentType contentType; final String filename = TextUtils.toLowerCase(file.getName()); if (filename.endsWith(".txt")) { contentType = ContentType.TEXT_PLAIN; } else if (filename.endsWith(".html") || filename.endsWith(".htm")) { contentType = ContentType.TEXT_HTML; } else if (filename.endsWith(".xml")) { contentType = ContentType.TEXT_XML; } else { contentType = ContentType.DEFAULT_BINARY; } final HttpCoreContext coreContext = HttpCoreContext.adapt(context); final EndpointDetails endpoint = coreContext.getEndpointDetails(); println(endpoint + " | serving file " + file.getPath()); responseTrigger.submitResponse( AsyncResponseBuilder.create(HttpStatus.SC_OK) .setEntity(AsyncEntityProducers.create(file, contentType)) .build(), context); } } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { println("HTTP server shutting down"); server.close(CloseMode.GRACEFUL); })); server.start(); final Future future = server.listen(new InetSocketAddress(port), URIScheme.HTTP); final ListenerEndpoint listenerEndpoint = future.get(); println("Listening on " + listenerEndpoint.getAddress()); server.awaitShutdown(TimeValue.MAX_VALUE); } static void println(final String msg) { System.out.println(HttpDateGenerator.INSTANCE.getCurrentDate() + " | " + msg); } } httpcore5/src/test/java/org/apache/hc/core5/http/examples/AsyncRequestExecutionExample.java0100664 0000000 0000000 00000013454 14403631147 031132 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.bootstrap.AsyncRequesterBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.Timeout; /** * Example of asynchronous HTTP/1.1 request execution. */ public class AsyncRequestExecutionExample { public static void main(final String[] args) throws Exception { final IOReactorConfig ioReactorConfig = IOReactorConfig.custom() .setSoTimeout(5, TimeUnit.SECONDS) .build(); // Create and start requester final HttpAsyncRequester requester = AsyncRequesterBootstrap.bootstrap() .setIOReactorConfig(ioReactorConfig) .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { System.out.println(connection.getRemoteAddress() + " " + new RequestLine(request)); } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { System.out.println(connection.getRemoteAddress() + " " + new StatusLine(response)); } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (keepAlive) { System.out.println(connection.getRemoteAddress() + " exchange completed (connection kept alive)"); } else { System.out.println(connection.getRemoteAddress() + " exchange completed (connection closed)"); } } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP requester shutting down"); requester.close(CloseMode.GRACEFUL); })); requester.start(); final HttpHost target = new HttpHost("httpbin.org"); final String[] requestUris = new String[] {"/", "/ip", "/user-agent", "/headers"}; final CountDownLatch latch = new CountDownLatch(requestUris.length); for (final String requestUri: requestUris) { requester.execute( AsyncRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), Timeout.ofSeconds(5), new FutureCallback>() { @Override public void completed(final Message message) { final HttpResponse response = message.getHead(); final String body = message.getBody(); System.out.println(requestUri + "->" + response.getCode()); System.out.println(body); latch.countDown(); } @Override public void failed(final Exception ex) { System.out.println(requestUri + "->" + ex); latch.countDown(); } @Override public void cancelled() { System.out.println(requestUri + " cancelled"); latch.countDown(); } }); } latch.await(); System.out.println("Shutting down I/O reactor"); requester.initiateShutdown(); } } httpcore5/src/test/java/org/apache/hc/core5/http/examples/ClassicReverseProxyExample.java0100664 0000000 0000000 00000026337 14403631147 030603 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.examples; import java.io.IOException; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ExceptionListener; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.RequesterBootstrap; import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.pool.ConnPoolListener; import org.apache.hc.core5.pool.ConnPoolStats; import org.apache.hc.core5.pool.PoolStats; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Example of embedded HTTP/1.1 reverse proxy using classic I/O. */ public class ClassicReverseProxyExample { public static void main(final String[] args) throws Exception { if (args.length < 1) { System.out.println("Usage: [listener port]"); System.exit(1); } final HttpHost targetHost = HttpHost.create(args[0]); int port = 8080; if (args.length > 1) { port = Integer.parseInt(args[1]); } System.out.println("Reverse proxy to " + targetHost); final HttpRequester requester = RequesterBootstrap.bootstrap() .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { System.out.println("[proxy->origin] " + Thread.currentThread() + " " + request.getMethod() + " " + request.getRequestUri()); } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { System.out.println("[proxy<-origin] " + Thread.currentThread() + " status " + response.getCode()); } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { System.out.println("[proxy<-origin] " + Thread.currentThread() + " exchange completed; " + "connection " + (keepAlive ? "kept alive" : "cannot be kept alive")); } }) .setConnPoolListener(new ConnPoolListener() { @Override public void onLease(final HttpHost route, final ConnPoolStats connPoolStats) { final StringBuilder buf = new StringBuilder(); buf.append("[proxy->origin] ").append(Thread.currentThread()).append(" connection leased ").append(route); System.out.println(buf); } @Override public void onRelease(final HttpHost route, final ConnPoolStats connPoolStats) { final StringBuilder buf = new StringBuilder(); buf.append("[proxy->origin] ").append(Thread.currentThread()).append(" connection released ").append(route); final PoolStats totals = connPoolStats.getTotalStats(); buf.append("; total kept alive: ").append(totals.getAvailable()).append("; "); buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable()); buf.append(" of ").append(totals.getMax()); System.out.println(buf); } }) .create(); final HttpServer server = ServerBootstrap.bootstrap() .setListenerPort(port) .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { System.out.println("[client->proxy] " + Thread.currentThread() + " " + request.getMethod() + " " + request.getRequestUri()); } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { System.out.println("[client<-proxy] " + Thread.currentThread() + " status " + response.getCode()); } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { System.out.println("[client<-proxy] " + Thread.currentThread() + " exchange completed; " + "connection " + (keepAlive ? "kept alive" : "cannot be kept alive")); } }) .setExceptionListener(new ExceptionListener() { @Override public void onError(final Exception ex) { if (ex instanceof SocketException) { System.out.println("[client->proxy] " + Thread.currentThread() + " " + ex.getMessage()); } else { System.out.println("[client->proxy] " + Thread.currentThread() + " " + ex.getMessage()); ex.printStackTrace(System.out); } } @Override public void onError(final HttpConnection connection, final Exception ex) { if (ex instanceof SocketTimeoutException) { System.out.println("[client->proxy] " + Thread.currentThread() + " time out"); } else if (ex instanceof SocketException || ex instanceof ConnectionClosedException) { System.out.println("[client->proxy] " + Thread.currentThread() + " " + ex.getMessage()); } else { System.out.println("[client->proxy] " + Thread.currentThread() + " " + ex.getMessage()); ex.printStackTrace(System.out); } } }) .register("*", new ProxyHandler(targetHost, requester)) .create(); server.start(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { server.close(CloseMode.GRACEFUL); requester.close(CloseMode.GRACEFUL); })); System.out.println("Listening on port " + port); server.awaitTermination(TimeValue.MAX_VALUE); } private final static Set HOP_BY_HOP = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( TextUtils.toLowerCase(HttpHeaders.HOST), TextUtils.toLowerCase(HttpHeaders.CONTENT_LENGTH), TextUtils.toLowerCase(HttpHeaders.TRANSFER_ENCODING), TextUtils.toLowerCase(HttpHeaders.CONNECTION), TextUtils.toLowerCase(HttpHeaders.KEEP_ALIVE), TextUtils.toLowerCase(HttpHeaders.PROXY_AUTHENTICATE), TextUtils.toLowerCase(HttpHeaders.TE), TextUtils.toLowerCase(HttpHeaders.TRAILER), TextUtils.toLowerCase(HttpHeaders.UPGRADE)))); static class ProxyHandler implements HttpRequestHandler { private final HttpHost targetHost; private final HttpRequester requester; public ProxyHandler( final HttpHost targetHost, final HttpRequester requester) { super(); this.targetHost = targetHost; this.requester = requester; } @Override public void handle( final ClassicHttpRequest incomingRequest, final ClassicHttpResponse outgoingResponse, final HttpContext serverContext) throws HttpException, IOException { final HttpCoreContext clientContext = HttpCoreContext.create(); final ClassicHttpRequest outgoingRequest = new BasicClassicHttpRequest( incomingRequest.getMethod(), targetHost, incomingRequest.getPath()); for (final Iterator
it = incomingRequest.headerIterator(); it.hasNext(); ) { final Header header = it.next(); if (!HOP_BY_HOP.contains(TextUtils.toLowerCase(header.getName()))) { outgoingRequest.addHeader(header); } } outgoingRequest.setEntity(incomingRequest.getEntity()); final ClassicHttpResponse incomingResponse = requester.execute( targetHost, outgoingRequest, Timeout.ofMinutes(1), clientContext); outgoingResponse.setCode(incomingResponse.getCode()); for (final Iterator
it = incomingResponse.headerIterator(); it.hasNext(); ) { final Header header = it.next(); if (!HOP_BY_HOP.contains(TextUtils.toLowerCase(header.getName()))) { outgoingResponse.addHeader(header); } } outgoingResponse.setEntity(incomingResponse.getEntity()); } } } httpcore5/src/test/java/org/apache/hc/core5/http/ReadableByteChannelMock.java0100664 0000000 0000000 00000006012 14315123013 026072 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.ReadableByteChannel; import java.nio.charset.Charset; public class ReadableByteChannelMock implements ReadableByteChannel { private final byte[][] chunks; private int chunkCount = 0; private ByteBuffer currentChunk; private boolean eof = false; private boolean closed = false; public ReadableByteChannelMock(final String[] strings, final Charset charset) { super(); this.chunks = new byte[strings.length][]; for (int i = 0; i < strings.length; i++) { this.chunks[i] = strings[i].getBytes(charset); } } public ReadableByteChannelMock(final byte[]... chunks) { super(); this.chunks = chunks; } private void prepareChunk() { if (this.currentChunk == null || !this.currentChunk.hasRemaining()) { if (this.chunkCount < this.chunks.length) { final byte[] bytes = this.chunks[this.chunkCount]; this.chunkCount++; this.currentChunk = ByteBuffer.wrap(bytes); } else { this.eof = true; } } } @Override public int read(final ByteBuffer dst) throws IOException { if (this.closed) { throw new ClosedChannelException(); } prepareChunk(); if (this.eof) { return -1; } int i = 0; while (dst.hasRemaining() && this.currentChunk.hasRemaining()) { dst.put(this.currentChunk.get()); i++; } return i; } @Override public void close() throws IOException { this.closed = true; } @Override public boolean isOpen() { return !this.closed && !this.eof; } } httpcore5/src/test/java/org/apache/hc/core5/http/HeaderMatcher.java0100664 0000000 0000000 00000004450 14403631147 024156 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.util.Objects; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; public class HeaderMatcher extends BaseMatcher
{ private final String headerName; private final Object headerValue; public HeaderMatcher(final String headerName, final Object headerValue) { this.headerName = headerName; this.headerValue = headerValue; } @Override public boolean matches(final Object item) { if (item instanceof Header) { final Header header = (Header) item; if (headerName.equalsIgnoreCase(header.getName()) && Objects.equals(headerValue, header.getValue())) { return true; } } return false; } @Override public void describeTo(final Description description) { description.appendText("same header as ").appendValue(headerValue).appendText(": ").appendValue(headerValue); } public static Matcher
same(final String headerName, final Object headerValue) { return new HeaderMatcher(headerName, headerValue); } } httpcore5/src/test/java/org/apache/hc/core5/http/ssl/0040775 0000000 0000000 00000000000 14403631147 021420 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TLSTest.java0100664 0000000 0000000 00000005260 14403631147 023565 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.ssl; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.hc.core5.http.ParseException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class TLSTest { @Test void isSame() throws ParseException { assertTrue(TLS.V_1_0.isSame(TLS.parse("TLSv1"))); } @Test void isComparable() throws ParseException { assertTrue(TLS.V_1_0.isComparable(TLS.parse("TLSv1"))); } @Test void greaterEquals() throws ParseException { assertTrue(TLS.V_1_3.greaterEquals(TLS.parse("TLSv1"))); } @Test void lessEquals() throws ParseException { assertTrue(TLS.V_1_0.lessEquals(TLS.parse("TLSv1.3"))); } @Test void parse() throws ParseException { assertTrue(TLS.V_1_0.lessEquals(TLS.parse("TLSv1.3"))); } @Test void parseNull() throws ParseException { assertNull(TLS.parse(null)); } @Test void excludeWeakNull() { assertNull((TLS.excludeWeak((String[]) null))); } @Test void excludeWeak() { final String[] mixProtocol = { "SSL 2.0", "TLS 1.3", "SSL 3.0", "TLS 1.2", "TLS 1.1" }; final String[] strongProtocols = TLS.excludeWeak(mixProtocol); for (final String protocol : strongProtocols) { Assertions.assertTrue(TLS.isSecure(protocol)); } } } httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java0100664 0000000 0000000 00000011731 14403631147 025203 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.ssl; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link TlsCiphers}. */ public class TestTlsCiphers { @Test public void testStrongCipherSuites() { final String[] strongCipherSuites = { "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_GCM_SHA384" }; for (final String cipherSuite : strongCipherSuites) { Assertions.assertFalse(TlsCiphers.isWeak(cipherSuite)); } } @Test public void testWeakCiphersDisabledByDefault() { final String[] weakCiphersSuites = { "SSL_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_128_CBC_SHA", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_RSA_WITH_NULL_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_256_GCM_SHA384", "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_NULL_SHA256", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5" }; for (final String cipherSuite : weakCiphersSuites) { Assertions.assertTrue(TlsCiphers.isWeak(cipherSuite)); } } @Test void excludeH2Blacklisted (){ final String[] mixCipherSuites = { "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_RSA_WITH_AES_256_CBC_SHA256", "AES_SHA_US", "TLS_RSA_WITH_AES_128_CBC_SHA", "NULL_SHA", "TLS_RSA_WITH_AES_256_GCM_SHA384" }; final String[] strongCipherSuites = TlsCiphers.excludeH2Blacklisted(mixCipherSuites); for (final String cipherSuite : strongCipherSuites) { Assertions.assertFalse(TlsCiphers.isWeak(cipherSuite)); } } @Test void excludeWeak (){ final String[] weakCiphersSuites = { "SSL_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_128_CBC_SHA", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_RSA_WITH_NULL_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_256_GCM_SHA384", "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_NULL_SHA256", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_GCM_SHA384" }; final String[] strongCipherSuites = TlsCiphers.excludeWeak(weakCiphersSuites); for (final String cipherSuite : strongCipherSuites) { Assertions.assertFalse(TlsCiphers.isWeak(cipherSuite)); } } @Test void excludeWeakNull(){ Assertions.assertNull(TlsCiphers.excludeWeak((String[]) null)); } } httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsVersionParser.java0100664 0000000 0000000 00000006671 14403631147 026417 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.ssl; import static org.hamcrest.MatcherAssert.assertThat; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Tokenizer; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Unit tests for {@link TlsVersionParser}. */ public class TestTlsVersionParser { private TlsVersionParser impl; @BeforeEach public void setup() { impl = new TlsVersionParser(); } @Test public void testParseBasic() throws Exception { assertThat(impl.parse("TLSv1"), CoreMatchers.equalTo(TLS.V_1_0.getVersion())); assertThat(impl.parse("TLSv1.1"), CoreMatchers.equalTo(TLS.V_1_1.getVersion())); assertThat(impl.parse("TLSv1.2"), CoreMatchers.equalTo(TLS.V_1_2.getVersion())); assertThat(impl.parse("TLSv1.3"), CoreMatchers.equalTo(TLS.V_1_3.getVersion())); assertThat(impl.parse("TLSv22.356"), CoreMatchers.equalTo(new ProtocolVersion("TLS", 22, 356))); } @Test public void testParseBuffer() throws Exception { final Tokenizer.Cursor cursor = new Tokenizer.Cursor(1, 13); assertThat(impl.parse(" TLSv1.2,0000", cursor, Tokenizer.INIT_BITSET(',')), CoreMatchers.equalTo(TLS.V_1_2.getVersion())); assertThat(cursor.getPos(), CoreMatchers.equalTo(8)); } @Test public void testParseFailure1() throws Exception { Assertions.assertThrows(ParseException.class, () -> impl.parse("Tlsv1")); } @Test public void testParseFailure2() throws Exception { Assertions.assertThrows(ParseException.class, () -> impl.parse("TLSV1")); } @Test public void testParseFailure3() throws Exception { Assertions.assertThrows(ParseException.class, () -> impl.parse("TLSv")); } @Test public void testParseFailure4() throws Exception { Assertions.assertThrows(ParseException.class, () -> impl.parse("TLSv1A")); } @Test public void testParseFailure5() throws Exception { Assertions.assertThrows(ParseException.class, () -> impl.parse("TLSv1.A")); } } httpcore5/src/test/java/org/apache/hc/core5/http/HeadersMatcher.java0100664 0000000 0000000 00000004757 14403631147 024353 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.util.Objects; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; public class HeadersMatcher extends BaseMatcher { private final Header[] expectedHeaders; public HeadersMatcher(final Header... headers) { this.expectedHeaders = headers; } @Override public boolean matches(final Object item) { if (item instanceof Header[]) { final Header[] headers = (Header[]) item; if (headers.length == expectedHeaders.length) { for (int i = 0; i < headers.length; i++) { final Header h1 = headers[i]; final Header h2 = expectedHeaders[i]; if (!h1.getName().equalsIgnoreCase(h2.getName()) || !Objects.equals(h1.getValue(), h2.getValue())) { return false; } } return true; } } return false; } @Override public void describeTo(final Description description) { description.appendText("same headers as ").appendValueList("[", "; ", "]", expectedHeaders); } public static Matcher same(final Header... headers) { return new HeadersMatcher(headers); } } httpcore5/src/test/java/org/apache/hc/core5/http/TestHttpHost.java0100664 0000000 0000000 00000027454 14403631147 024110 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link HttpHost}. * */ public class TestHttpHost { @Test public void testConstructor() { final HttpHost host1 = new HttpHost("somehost"); Assertions.assertEquals("somehost", host1.getHostName()); Assertions.assertEquals(-1, host1.getPort()); Assertions.assertEquals("http", host1.getSchemeName()); final HttpHost host2 = new HttpHost("somehost", 8080); Assertions.assertEquals("somehost", host2.getHostName()); Assertions.assertEquals(8080, host2.getPort()); Assertions.assertEquals("http", host2.getSchemeName()); final HttpHost host3 = new HttpHost("somehost", -1); Assertions.assertEquals("somehost", host3.getHostName()); Assertions.assertEquals(-1, host3.getPort()); Assertions.assertEquals("http", host3.getSchemeName()); final HttpHost host4 = new HttpHost("https", "somehost", 443); Assertions.assertEquals("somehost", host4.getHostName()); Assertions.assertEquals(443, host4.getPort()); Assertions.assertEquals("https", host4.getSchemeName()); final HttpHost host5 = new HttpHost("https", "somehost"); Assertions.assertEquals("somehost", host5.getHostName()); Assertions.assertEquals(-1, host5.getPort()); Assertions.assertEquals("https", host5.getSchemeName()); Assertions.assertThrows(NullPointerException.class, () -> new HttpHost(null, (String) null, -1)); Assertions.assertThrows(IllegalArgumentException.class, () -> new HttpHost(null, " ", -1)); Assertions.assertThrows(NullPointerException.class, () -> new HttpHost(null, (InetAddress) null, -1)); } @Test public void testHashCode() throws Exception { final HttpHost host1 = new HttpHost("http", "somehost", 8080); final HttpHost host2 = new HttpHost("http", "somehost", 80); final HttpHost host3 = new HttpHost("http", "someotherhost", 8080); final HttpHost host4 = new HttpHost("http", "somehost", 80); final HttpHost host5 = new HttpHost("http", "SomeHost", 80); final HttpHost host6 = new HttpHost("myhttp", "SomeHost", 80); final HttpHost host7 = new HttpHost( "http", InetAddress.getByAddress("127.0.0.1", new byte[] {127,0,0,1}), 80); final HttpHost host8 = new HttpHost("http", "127.0.0.1", 80); final HttpHost host9 = new HttpHost( "http", InetAddress.getByAddress("somehost",new byte[] {127,0,0,1}), 80); final HttpHost host10 = new HttpHost( "http", InetAddress.getByAddress(new byte[] {127,0,0,1}), "somehost", 80); final HttpHost host11 = new HttpHost( "http", InetAddress.getByAddress("someotherhost",new byte[] {127,0,0,1}), 80); Assertions.assertEquals(host1.hashCode(), host1.hashCode()); Assertions.assertTrue(host1.hashCode() != host2.hashCode()); Assertions.assertTrue(host1.hashCode() != host3.hashCode()); Assertions.assertEquals(host2.hashCode(), host4.hashCode()); Assertions.assertEquals(host2.hashCode(), host5.hashCode()); Assertions.assertTrue(host5.hashCode() != host6.hashCode()); Assertions.assertTrue(host7.hashCode() != host8.hashCode()); Assertions.assertTrue(host8.hashCode() != host9.hashCode()); Assertions.assertEquals(host9.hashCode(), host10.hashCode()); Assertions.assertTrue(host10.hashCode() != host11.hashCode()); Assertions.assertTrue(host9.hashCode() != host11.hashCode()); } @Test public void testEquals() throws Exception { final HttpHost host1 = new HttpHost("http", "somehost", 8080); final HttpHost host2 = new HttpHost("http", "somehost", 80); final HttpHost host3 = new HttpHost("http", "someotherhost", 8080); final HttpHost host4 = new HttpHost("http", "somehost", 80); final HttpHost host5 = new HttpHost("http", "SomeHost", 80); final HttpHost host6 = new HttpHost("myhttp", "SomeHost", 80); final HttpHost host7 = new HttpHost( "http", InetAddress.getByAddress("127.0.0.1", new byte[] {127,0,0,1}), 80); final HttpHost host8 = new HttpHost("http", "127.0.0.1", 80); final HttpHost host9 = new HttpHost( "http", InetAddress.getByAddress("somehost", new byte[] {127,0,0,1}), 80); final HttpHost host10 = new HttpHost( "http", InetAddress.getByAddress(new byte[] {127,0,0,1}), "somehost", 80); final HttpHost host11 = new HttpHost( "http", InetAddress.getByAddress("someotherhost",new byte[] {127,0,0,1}), 80); Assertions.assertEquals(host1, host1); Assertions.assertNotEquals(host1, host2); Assertions.assertNotEquals(host1, host3); Assertions.assertEquals(host2, host4); Assertions.assertEquals(host2, host5); Assertions.assertNotEquals(host5, host6); Assertions.assertNotEquals(host7, host8); Assertions.assertFalse(host7.equals(host9)); Assertions.assertNotEquals(null, host1); Assertions.assertNotEquals("http://somehost", host1); Assertions.assertNotEquals("http://somehost", host9); Assertions.assertNotEquals(host8, host9); Assertions.assertEquals(host9, host10); Assertions.assertNotEquals(host9, host11); } @Test public void testToString() throws Exception { final HttpHost host1 = new HttpHost("somehost"); Assertions.assertEquals("http://somehost", host1.toString()); final HttpHost host2 = new HttpHost("somehost", -1); Assertions.assertEquals("http://somehost", host2.toString()); final HttpHost host3 = new HttpHost("somehost", -1); Assertions.assertEquals("http://somehost", host3.toString()); final HttpHost host4 = new HttpHost("somehost", 8888); Assertions.assertEquals("http://somehost:8888", host4.toString()); final HttpHost host5 = new HttpHost("myhttp", "somehost", -1); Assertions.assertEquals("myhttp://somehost", host5.toString()); final HttpHost host6 = new HttpHost("myhttp", "somehost", 80); Assertions.assertEquals("myhttp://somehost:80", host6.toString()); final HttpHost host7 = new HttpHost( "http", InetAddress.getByAddress("127.0.0.1", new byte[] {127,0,0,1}), 80); Assertions.assertEquals("http://127.0.0.1:80", host7.toString()); final HttpHost host9 = new HttpHost( "http", InetAddress.getByAddress("somehost", new byte[] {127,0,0,1}), 80); Assertions.assertEquals("http://somehost:80", host9.toString()); } @Test public void testToHostString() { final HttpHost host1 = new HttpHost("somehost"); Assertions.assertEquals("somehost", host1.toHostString()); final HttpHost host2 = new HttpHost("somehost"); Assertions.assertEquals("somehost", host2.toHostString()); final HttpHost host3 = new HttpHost("somehost", -1); Assertions.assertEquals("somehost", host3.toHostString()); final HttpHost host4 = new HttpHost("somehost", 8888); Assertions.assertEquals("somehost:8888", host4.toHostString()); } @Test public void testSerialization() throws Exception { final HttpHost orig = new HttpHost("https", "somehost", 8080); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer); outStream.writeObject(orig); outStream.close(); final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final HttpHost clone = (HttpHost) inStream.readObject(); Assertions.assertEquals(orig, clone); } @Test public void testCreateFromString() throws Exception { Assertions.assertEquals(new HttpHost("https", "somehost", 8080), HttpHost.create("https://somehost:8080")); Assertions.assertEquals(new HttpHost("https", "somehost", 8080), HttpHost.create("HttpS://SomeHost:8080")); Assertions.assertEquals(new HttpHost(null, "somehost", 1234), HttpHost.create("somehost:1234")); Assertions.assertEquals(new HttpHost(null, "somehost", -1), HttpHost.create("somehost")); } @Test public void testCreateFromURI() throws Exception { Assertions.assertEquals(new HttpHost("https", "somehost", 8080), HttpHost.create(URI.create("https://somehost:8080"))); Assertions.assertEquals(new HttpHost("https", "somehost", 8080), HttpHost.create(URI.create("HttpS://SomeHost:8080"))); Assertions.assertEquals(new HttpHost("https", "somehost", 8080), HttpHost.create(URI.create("HttpS://SomeHost:8080/foo"))); } @Test public void testCreateFromStringInvalid() throws Exception { Assertions.assertThrows(URISyntaxException.class, () -> HttpHost.create(" host ")); Assertions.assertThrows(URISyntaxException.class, () -> HttpHost.create("host :8080")); Assertions.assertThrows(IllegalArgumentException.class, () -> HttpHost.create("")); } @Test public void testIpv6HostAndPort() throws Exception { final HttpHost host = HttpHost.create("[::1]:80"); Assertions.assertEquals("http", host.getSchemeName()); Assertions.assertEquals("::1", host.getHostName()); Assertions.assertEquals(80, host.getPort()); } @Test public void testIpv6HostAndPortWithScheme() throws Exception { final HttpHost host = HttpHost.create("https://[::1]:80"); Assertions.assertEquals("https", host.getSchemeName()); Assertions.assertEquals("::1", host.getHostName()); Assertions.assertEquals(80, host.getPort()); } @Test public void testIpv6HostAndPortWithoutBrackets() throws Exception { Assertions.assertThrows(URISyntaxException.class, () -> HttpHost.create("::1:80")); } @Test public void testIpv6HostWithoutPort() throws Exception { Assertions.assertThrows(URISyntaxException.class, () -> HttpHost.create("::1")); } @Test public void testIpv6HostToString() { Assertions.assertEquals("http://[::1]:80", new HttpHost("::1", 80).toString()); Assertions.assertEquals("http://[::1]", new HttpHost("::1", -1).toString()); } } httpcore5/src/test/java/org/apache/hc/core5/http/io/0040775 0000000 0000000 00000000000 14403631147 021226 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/io/support/0040775 0000000 0000000 00000000000 14403631147 022742 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilderTest.java0100664 0000000 0000000 00000030216 14403631147 030705 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.support; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.net.URISyntaxException; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.net.URIBuilder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ClassicRequestBuilderTest { @Test void constructor() throws UnknownHostException, URISyntaxException { final ClassicRequestBuilder classicRequestBuilder = new ClassicRequestBuilder(Method.HEAD); assertEquals(Method.HEAD.name(), classicRequestBuilder.getMethod()); final ClassicRequestBuilder classicRequestBuilder2 = new ClassicRequestBuilder(Method.HEAD.name()); assertEquals(Method.HEAD.name(), classicRequestBuilder2.getMethod()); final ClassicRequestBuilder classicRequestBuilder3 = new ClassicRequestBuilder(Method.HEAD.name(), URIBuilder.localhost().build()); assertEquals(Method.HEAD.name(), classicRequestBuilder3.getMethod()); assertEquals(URIBuilder.localhost().getHost(), classicRequestBuilder3.getAuthority().getHostName()); final ClassicRequestBuilder classicRequestBuilder4 = new ClassicRequestBuilder(Method.HEAD, URIBuilder.localhost().build()); assertEquals(Method.HEAD.name(), classicRequestBuilder4.getMethod()); assertEquals(URIBuilder.localhost().getHost(), classicRequestBuilder4.getAuthority().getHostName()); final ClassicRequestBuilder classicRequestBuilder5 = new ClassicRequestBuilder(Method.HEAD, "/localhost"); assertEquals(Method.HEAD.name(), classicRequestBuilder5.getMethod()); assertEquals("/localhost", classicRequestBuilder5.getPath()); final ClassicRequestBuilder classicRequestBuilder6 = new ClassicRequestBuilder(Method.HEAD.name(), "/localhost"); assertEquals(Method.HEAD.name(), classicRequestBuilder6.getMethod()); assertEquals("/localhost", classicRequestBuilder6.getPath()); } @Test public void create() { final ClassicHttpRequest classicHttpRequest = ClassicRequestBuilder.create(Method.HEAD.name()).build(); assertEquals(Method.HEAD.name(), classicHttpRequest.getMethod()); } @Test public void get() throws UnknownHostException, URISyntaxException { final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.get(); assertEquals(Method.GET.name(), classicRequestBuilder.getMethod()); final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.get(URIBuilder.localhost().build()); assertEquals(Method.GET.name(), classicRequestBuilder1.getMethod()); final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.get("/localhost"); assertEquals(Method.GET.name(), classicRequestBuilder3.getMethod()); assertEquals("/localhost", classicRequestBuilder3.getPath()); } @Test public void head() throws UnknownHostException, URISyntaxException { final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.head(); assertEquals(Method.HEAD.name(), classicRequestBuilder.getMethod()); final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.head(URIBuilder.localhost().build()); assertEquals(Method.HEAD.name(), classicRequestBuilder1.getMethod()); final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.head("/localhost"); assertEquals(Method.HEAD.name(), classicRequestBuilder3.getMethod()); assertEquals("/localhost", classicRequestBuilder3.getPath()); } @Test public void patch() throws UnknownHostException, URISyntaxException { final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.patch(); assertEquals(Method.PATCH.name(), classicRequestBuilder.getMethod()); final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.patch(URIBuilder.localhost().build()); assertEquals(Method.PATCH.name(), classicRequestBuilder1.getMethod()); final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.patch("/localhost"); assertEquals(Method.PATCH.name(), classicRequestBuilder3.getMethod()); assertEquals("/localhost", classicRequestBuilder3.getPath()); } @Test public void post() throws UnknownHostException, URISyntaxException { final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.post(); assertEquals(Method.POST.name(), classicRequestBuilder.getMethod()); final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.post(URIBuilder.localhost().build()); assertEquals(Method.POST.name(), classicRequestBuilder1.getMethod()); final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.post("/localhost"); assertEquals(Method.POST.name(), classicRequestBuilder3.getMethod()); assertEquals("/localhost", classicRequestBuilder3.getPath()); } @Test public void put() throws UnknownHostException, URISyntaxException { final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.put(); assertEquals(Method.PUT.name(), classicRequestBuilder.getMethod()); final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.put(URIBuilder.localhost().build()); assertEquals(Method.PUT.name(), classicRequestBuilder1.getMethod()); final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.put("/localhost"); assertEquals(Method.PUT.name(), classicRequestBuilder3.getMethod()); assertEquals("/localhost", classicRequestBuilder3.getPath()); } @Test public void delete() throws UnknownHostException, URISyntaxException { final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.delete(); assertEquals(Method.DELETE.name(), classicRequestBuilder.getMethod()); final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.delete(URIBuilder.localhost().build()); assertEquals(Method.DELETE.name(), classicRequestBuilder1.getMethod()); final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.delete("/localhost"); assertEquals(Method.DELETE.name(), classicRequestBuilder3.getMethod()); assertEquals("/localhost", classicRequestBuilder3.getPath()); } @Test public void trace() throws UnknownHostException, URISyntaxException { final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.trace(); assertEquals(Method.TRACE.name(), classicRequestBuilder.getMethod()); final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.trace(URIBuilder.localhost().build()); assertEquals(Method.TRACE.name(), classicRequestBuilder1.getMethod()); final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.trace("/localhost"); assertEquals(Method.TRACE.name(), classicRequestBuilder3.getMethod()); assertEquals("/localhost", classicRequestBuilder3.getPath()); } @Test public void option() throws UnknownHostException, URISyntaxException { final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.options(); assertEquals(Method.OPTIONS.name(), classicRequestBuilder.getMethod()); final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.options(URIBuilder.localhost().build()); assertEquals(Method.OPTIONS.name(), classicRequestBuilder1.getMethod()); final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.options("/localhost"); assertEquals(Method.OPTIONS.name(), classicRequestBuilder3.getMethod()); assertEquals("/localhost", classicRequestBuilder3.getPath()); } @Test public void builder() { final Header header = new BasicHeader("header2", "blah"); final ClassicHttpRequest classicHttpRequest = ClassicRequestBuilder.get() .setVersion(HttpVersion.HTTP_1_1) .setCharset(StandardCharsets.US_ASCII) .setAuthority(new URIAuthority("host")) .setEntity("

Access denied

", ContentType.TEXT_HTML) .setHeader(new BasicHeader("header2", "blah")) .setHeader("X-Test-Filter", "active") .setHeader(header) .setPath("path/") .setScheme("http") .addHeader(header) .addHeader("header", ".addHeader(header)") .addParameter(new BasicHeader("header2", "blah")) .addParameter("param1", "value1") .addParameters(new BasicNameValuePair("param3", "value3"), new BasicNameValuePair("param4", null)) .setAbsoluteRequestUri(true) .setEntity(new StringEntity("requestBody")) .setEntity(new ByteArrayEntity(new byte[10240], ContentType.TEXT_PLAIN)) .setEntity(new byte[10240], ContentType.TEXT_HTML) .setEntity("requestBody") .setUri("theUri") .setHttpHost(new HttpHost("httpbin.org")) .build(); assertAll("Should return address of Oracle's headquarter", () -> assertNotNull(classicHttpRequest.getEntity()), () -> assertEquals(Method.GET.name(), classicHttpRequest.getMethod()), () -> assertEquals("http", classicHttpRequest.getScheme()), () -> assertEquals("httpbin.org", classicHttpRequest.getAuthority().getHostName()), () -> assertEquals(HttpVersion.HTTP_1_1, classicHttpRequest.getVersion()), () -> assertEquals(4, classicHttpRequest.getHeaders().length), () -> assertNotNull(ClassicRequestBuilder.get().toString()), () -> assertEquals("http://httpbin.org/theUri?header2=blah¶m1=value1¶m3=value3¶m4", new String(classicHttpRequest.getRequestUri().getBytes())) ); } @Test public void builderTraceThrowsIllegalStateException() { Assertions.assertThrows(IllegalStateException.class, () -> ClassicRequestBuilder.trace() .setVersion(HttpVersion.HTTP_1_1) .setEntity(new ByteArrayEntity(new byte[10240], ContentType.TEXT_PLAIN)) .build()); } }httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicResponseBuilderTest.java0100664 0000000 0000000 00000015047 14403631147 031060 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.support; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.Socket; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnection; import org.apache.hc.core5.http.impl.io.DefaultHttpRequestParserFactory; import org.apache.hc.core5.http.impl.io.DefaultHttpResponseWriterFactory; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicHeader; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; class ClassicResponseBuilderTest { @Mock Socket socket; DefaultBHttpServerConnection conn; ByteArrayOutputStream outStream; @BeforeEach public void prepareMocks() throws IOException { MockitoAnnotations.openMocks(this); conn = new DefaultBHttpServerConnection("http", Http1Config.DEFAULT, null, null, DefaultContentLengthStrategy.INSTANCE, DefaultContentLengthStrategy.INSTANCE, DefaultHttpRequestParserFactory.INSTANCE, DefaultHttpResponseWriterFactory.INSTANCE); outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); } @Test void create() throws IOException, HttpException { final ClassicHttpResponse response = ClassicResponseBuilder.create(200) .setHeader("X-Test-Filter", "active") .addHeader("header1", "blah") .setHeader(new BasicHeader("header2", "blah")) .addHeader(new BasicHeader("header3", "blah")) .setVersion(HttpVersion.HTTP_1_1) .setEntity("

Access OK

", ContentType.TEXT_HTML) .setEntity("Another entity") .build(); response.addHeader("User-Agent", "test"); conn.sendResponseHeader(response); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("HTTP/1.1 200 OK\r\nX-Test-Filter: active\r\nheader1: blah\r\nheader2: blah\r\nheader3: blah\r\nUser-Agent: test\r\n\r\n", s); Assertions.assertNotNull(response.getEntity()); } @Test void remove() throws IOException, HttpException { final Header header = new BasicHeader("header2", "blah"); final ClassicHttpResponse response = ClassicResponseBuilder.create(200) .setEntity(new StringEntity("123", ContentType.TEXT_PLAIN)) .setHeader("X-Test-Filter", "active") .addHeader("header1", "blah") .setHeader(header) .addHeader(new BasicHeader("header3", "blah")) .setVersion(HttpVersion.HTTP_1_1) .setEntity("

Access OK

", ContentType.TEXT_HTML) .setEntity("Another entity") .removeHeader(header) .build(); response.removeHeaders("X-Test-Filter"); conn.sendResponseHeader(response); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("HTTP/1.1 200 OK\r\nheader1: blah\r\nheader3: blah\r\n\r\n", s); Assertions.assertNotNull(response.getEntity()); } @Test void copy() throws IOException, HttpException { final Header header = new BasicHeader("header2", "blah"); final ClassicHttpResponse response = ClassicResponseBuilder.create(200) .setEntity(new StringEntity("123", ContentType.TEXT_PLAIN)) .addHeader("header1", "blah") .setHeader(header) .addHeader(new BasicHeader("header3", "blah")) .setVersion(HttpVersion.HTTP_1_1) .setEntity("

Access OK

", ContentType.TEXT_HTML) .setEntity("Another entity") .removeHeader(header) .build(); final ClassicResponseBuilder classicResponseBuilder = ClassicResponseBuilder.copy(response); final ClassicHttpResponse response2 = classicResponseBuilder.build(); conn.sendResponseHeader(response2); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("HTTP/1.1 200 OK\r\nheader1: blah\r\nheader3: blah\r\n\r\n", s); Assertions.assertNotNull(response.getEntity()); Assertions.assertNotNull(classicResponseBuilder.toString()); } }httpcore5/src/test/java/org/apache/hc/core5/http/io/TestEofSensorInputStream.java0100664 0000000 0000000 00000016174 14403631147 027036 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io; import java.io.IOException; import java.io.InputStream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @SuppressWarnings({"boxing","static-access"}) // test code public class TestEofSensorInputStream { private InputStream inStream; private EofSensorWatcher eofwatcher; private EofSensorInputStream eofstream; @BeforeEach public void setup() throws Exception { inStream = Mockito.mock(InputStream.class); eofwatcher = Mockito.mock(EofSensorWatcher.class); eofstream = new EofSensorInputStream(inStream, eofwatcher); } @Test public void testClose() throws Exception { Mockito.when(eofwatcher.streamClosed(Mockito.any())).thenReturn(Boolean.TRUE); eofstream.close(); Assertions.assertTrue(eofstream.isSelfClosed()); Assertions.assertNull(eofstream.getWrappedStream()); Mockito.verify(inStream, Mockito.times(1)).close(); Mockito.verify(eofwatcher).streamClosed(inStream); eofstream.close(); } @Test public void testCloseIOError() throws Exception { Mockito.when(eofwatcher.streamClosed(Mockito.any())).thenThrow(new IOException()); Assertions.assertThrows(IOException.class, () -> eofstream.close()); Assertions.assertTrue(eofstream.isSelfClosed()); Assertions.assertNull(eofstream.getWrappedStream()); Mockito.verify(eofwatcher).streamClosed(inStream); } @Test public void testReleaseConnection() throws Exception { Mockito.when(eofwatcher.streamClosed(Mockito.any())).thenReturn(Boolean.TRUE); eofstream.close(); Assertions.assertTrue(eofstream.isSelfClosed()); Assertions.assertNull(eofstream.getWrappedStream()); Mockito.verify(inStream, Mockito.times(1)).close(); Mockito.verify(eofwatcher).streamClosed(inStream); eofstream.close(); } @Test public void testAbortConnection() throws Exception { Mockito.when(eofwatcher.streamAbort(Mockito.any())).thenReturn(Boolean.TRUE); eofstream.abort(); Assertions.assertTrue(eofstream.isSelfClosed()); Assertions.assertNull(eofstream.getWrappedStream()); Mockito.verify(inStream, Mockito.times(1)).close(); Mockito.verify(eofwatcher).streamAbort(inStream); eofstream.abort(); } @Test public void testAbortConnectionIOError() throws Exception { Mockito.when(eofwatcher.streamAbort(Mockito.any())).thenThrow(new IOException()); Assertions.assertThrows(IOException.class, () -> eofstream.abort()); Assertions.assertTrue(eofstream.isSelfClosed()); Assertions.assertNull(eofstream.getWrappedStream()); Mockito.verify(eofwatcher).streamAbort(inStream); } @Test public void testRead() throws Exception { Mockito.when(eofwatcher.eofDetected(Mockito.any())).thenReturn(Boolean.TRUE); Mockito.when(inStream.read()).thenReturn(0, -1); Assertions.assertEquals(0, eofstream.read()); Assertions.assertFalse(eofstream.isSelfClosed()); Assertions.assertNotNull(eofstream.getWrappedStream()); Mockito.verify(eofwatcher, Mockito.never()).eofDetected(inStream); Assertions.assertEquals(-1, eofstream.read()); Assertions.assertFalse(eofstream.isSelfClosed()); Assertions.assertNull(eofstream.getWrappedStream()); Mockito.verify(inStream, Mockito.times(1)).close(); Mockito.verify(eofwatcher).eofDetected(inStream); Assertions.assertEquals(-1, eofstream.read()); } @Test public void testReadIOError() throws Exception { Mockito.when(eofwatcher.eofDetected(Mockito.any())).thenReturn(Boolean.TRUE); Mockito.when(inStream.read()).thenThrow(new IOException()); Assertions.assertThrows(IOException.class, () -> eofstream.read()); Assertions.assertFalse(eofstream.isSelfClosed()); Assertions.assertNull(eofstream.getWrappedStream()); Mockito.verify(eofwatcher).streamAbort(inStream); } @Test public void testReadByteArray() throws Exception { Mockito.when(eofwatcher.eofDetected(Mockito.any())).thenReturn(Boolean.TRUE); Mockito.when(inStream.read(Mockito.any(), Mockito.anyInt(), Mockito.anyInt())) .thenReturn(1, -1); final byte[] tmp = new byte[1]; Assertions.assertEquals(1, eofstream.read(tmp)); Assertions.assertFalse(eofstream.isSelfClosed()); Assertions.assertNotNull(eofstream.getWrappedStream()); Mockito.verify(eofwatcher, Mockito.never()).eofDetected(inStream); Assertions.assertEquals(-1, eofstream.read(tmp)); Assertions.assertFalse(eofstream.isSelfClosed()); Assertions.assertNull(eofstream.getWrappedStream()); Mockito.verify(inStream, Mockito.times(1)).close(); Mockito.verify(eofwatcher).eofDetected(inStream); Assertions.assertEquals(-1, eofstream.read(tmp)); } @Test public void testReadByteArrayIOError() throws Exception { Mockito.when(eofwatcher.eofDetected(Mockito.any())).thenReturn(Boolean.TRUE); Mockito.when(inStream.read(Mockito.any(), Mockito.anyInt(), Mockito.anyInt())) .thenThrow(new IOException()); final byte[] tmp = new byte[1]; Assertions.assertThrows(IOException.class, () -> eofstream.read(tmp)); Assertions.assertFalse(eofstream.isSelfClosed()); Assertions.assertNull(eofstream.getWrappedStream()); Mockito.verify(eofwatcher).streamAbort(inStream); } @Test public void testReadAfterAbort() throws Exception { Mockito.when(eofwatcher.streamAbort(Mockito.any())).thenReturn(Boolean.TRUE); eofstream.abort(); Assertions.assertThrows(IOException.class, () -> eofstream.read()); final byte[] tmp = new byte[1]; Assertions.assertThrows(IOException.class, () -> eofstream.read(tmp)); } } httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/0040775 0000000 0000000 00000000000 14435411677 022553 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestBufferedHttpEntity.java0100664 0000000 0000000 00000012742 14403631147 030027 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link BufferedHttpEntity}. * */ public class TestBufferedHttpEntity { @Test public void testBufferingEntity() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); final BufferedHttpEntity entity = new BufferedHttpEntity( new InputStreamEntity(new ByteArrayInputStream(bytes), -1, null)); Assertions.assertEquals(bytes.length, entity.getContentLength()); Assertions.assertTrue(entity.isRepeatable()); Assertions.assertFalse(entity.isChunked()); Assertions.assertFalse(entity.isStreaming()); // test if we can obtain contain multiple times Assertions.assertNotNull(entity.getContent ()); Assertions.assertNotNull(entity.getContent ()); } @Test public void testWrappingEntity() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); final ByteArrayEntity httpentity = new ByteArrayEntity(bytes, null, true); try (final BufferedHttpEntity bufentity = new BufferedHttpEntity(httpentity)) { Assertions.assertEquals(bytes.length, bufentity.getContentLength()); Assertions.assertTrue(bufentity.isRepeatable()); Assertions.assertTrue(bufentity.isChunked()); Assertions.assertFalse(bufentity.isStreaming()); // test if we can obtain contain multiple times Assertions.assertNotNull(bufentity.getContent()); Assertions.assertNotNull(bufentity.getContent()); } } @Test public void testIllegalConstructor() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> new BufferedHttpEntity(null)); } @Test public void testWriteToBuffered() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); final InputStreamEntity httpentity = new InputStreamEntity(new ByteArrayInputStream(bytes), -1, null); try (final BufferedHttpEntity bufentity = new BufferedHttpEntity(httpentity)) { ByteArrayOutputStream out = new ByteArrayOutputStream(); bufentity.writeTo(out); byte[] bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } out = new ByteArrayOutputStream(); bufentity.writeTo(out); bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } Assertions.assertThrows(NullPointerException.class, () -> bufentity.writeTo(null)); } } @Test public void testWriteToWrapped() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); final ByteArrayEntity httpentity = new ByteArrayEntity(bytes, null); try (final BufferedHttpEntity bufentity = new BufferedHttpEntity(httpentity)) { ByteArrayOutputStream out = new ByteArrayOutputStream(); bufentity.writeTo(out); byte[] bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } out = new ByteArrayOutputStream(); bufentity.writeTo(out); bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } Assertions.assertThrows(NullPointerException.class, () -> bufentity.writeTo(null)); } } } httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestBasicHttpEntity.java0100664 0000000 0000000 00000006101 14403631147 027316 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link BasicHttpEntity}. * */ public class TestBasicHttpEntity { @Test public void testBasics() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); final BasicHttpEntity httpentity = new BasicHttpEntity(new ByteArrayInputStream(bytes), bytes.length, null); Assertions.assertEquals(bytes.length, httpentity.getContentLength()); Assertions.assertFalse(httpentity.isRepeatable()); Assertions.assertTrue(httpentity.isStreaming()); } @Test public void testToString() throws Exception { try (final BasicHttpEntity httpentity = new BasicHttpEntity(EmptyInputStream.INSTANCE, 10, ContentType.parseLenient("blah"), "yada", true)) { Assertions.assertEquals( "[Entity-Class: BasicHttpEntity, Content-Type: blah, Content-Encoding: yada, chunked: true]", httpentity.toString()); } } @Test public void testWriteTo() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); final BasicHttpEntity httpentity = new BasicHttpEntity(new ByteArrayInputStream(bytes), bytes.length, ContentType.TEXT_PLAIN); final ByteArrayOutputStream out = new ByteArrayOutputStream(); httpentity.writeTo(out); final byte[] bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } } } httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestNullEntity.java0100664 0000000 0000000 00000005526 14403631147 026361 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; public class TestNullEntity { @Test public void testLength() { assertEquals(0, NullEntity.INSTANCE.getContentLength()); } @Test public void testContentType() { assertNull(NullEntity.INSTANCE.getContentType()); } @Test public void testContentEncoding() { assertNull(NullEntity.INSTANCE.getContentEncoding()); } @Test public void testTrailerNames() { assertEquals(Collections.emptySet(), NullEntity.INSTANCE.getTrailerNames()); } @Test public void testContentStream() throws IOException { try (InputStream content = NullEntity.INSTANCE.getContent()) { assertEquals(-1, content.read()); } // Closing the resource should have no impact try (InputStream content = NullEntity.INSTANCE.getContent()) { assertEquals(-1, content.read()); } } @Test public void testWriteTo() throws IOException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); NullEntity.INSTANCE.writeTo(baos); assertEquals(0, baos.size()); } @Test public void testIsStreaming() { assertFalse(NullEntity.INSTANCE.isStreaming()); } @Test public void testIsChunked() { assertFalse(NullEntity.INSTANCE.isChunked()); } }httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestInputStreamEntity.java0100664 0000000 0000000 00000012530 14435411677 027724 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link InputStreamEntity}. * */ public class TestInputStreamEntity { @Test public void testBasics() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.ISO_8859_1); final InputStreamEntity entity = new InputStreamEntity(new ByteArrayInputStream(bytes), bytes.length, null); Assertions.assertEquals(bytes.length, entity.getContentLength()); Assertions.assertNotNull(entity.getContent()); Assertions.assertFalse(entity.isRepeatable()); Assertions.assertTrue(entity.isStreaming()); } @Test public void testNullConstructor() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> new InputStreamEntity(null, 0, null)); } @Test public void testUnknownLengthConstructor() throws Exception { try (final InputStreamEntity entity = new InputStreamEntity(EmptyInputStream.INSTANCE, null)) { Assertions.assertEquals(-1, entity.getContentLength()); } } @Test public void testWriteTo() throws Exception { final String message = "Message content"; final byte[] bytes = message.getBytes(StandardCharsets.ISO_8859_1); final InputStream inStream = new ByteArrayInputStream(bytes); try (final InputStreamEntity entity = new InputStreamEntity(inStream, bytes.length, ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1))) { final ByteArrayOutputStream out = new ByteArrayOutputStream(); entity.writeTo(out); final byte[] writtenBytes = out.toByteArray(); Assertions.assertNotNull(writtenBytes); Assertions.assertEquals(bytes.length, writtenBytes.length); final String s = new String(writtenBytes, StandardCharsets.ISO_8859_1.name()); Assertions.assertEquals(message, s); } } @Test public void testWriteToPartialContent() throws Exception { final String message = "Message content"; final byte[] bytes = message.getBytes(StandardCharsets.ISO_8859_1); final InputStream inStream = new ByteArrayInputStream(bytes); final int contentLength = 7; try (final InputStreamEntity entity = new InputStreamEntity(inStream, contentLength, ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1))) { final ByteArrayOutputStream out = new ByteArrayOutputStream(); entity.writeTo(out); final byte[] writtenBytes = out.toByteArray(); Assertions.assertNotNull(writtenBytes); Assertions.assertEquals(contentLength, writtenBytes.length); final String s = new String(writtenBytes, StandardCharsets.ISO_8859_1.name()); Assertions.assertEquals(message.substring(0, contentLength), s); } } @Test public void testWriteToUnknownLength() throws Exception { final String message = "Message content"; final byte[] bytes = message.getBytes(StandardCharsets.ISO_8859_1); final InputStreamEntity entity = new InputStreamEntity(new ByteArrayInputStream(bytes), ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); entity.writeTo(out); final byte[] writtenBytes = out.toByteArray(); Assertions.assertNotNull(writtenBytes); Assertions.assertEquals(bytes.length, writtenBytes.length); final String s = new String(writtenBytes, StandardCharsets.ISO_8859_1.name()); Assertions.assertEquals(message, s); } @Test public void testWriteToNull() throws Exception { try (final InputStreamEntity entity = new InputStreamEntity(EmptyInputStream.INSTANCE, 0, null)) { Assertions.assertThrows(NullPointerException.class, () -> entity.writeTo(null)); }} } httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestSerializableEntity.java0100664 0000000 0000000 00000006433 14403631147 030053 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestSerializableEntity { public static class SerializableObject implements Serializable { private static final long serialVersionUID = 1833335861188359573L; public final int intValue = 4; public final String stringValue = "Hello"; public SerializableObject() {} } @Test public void testBasics() throws Exception { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ObjectOutputStream out = new ObjectOutputStream(baos); final Serializable serializableObj = new SerializableObject(); out.writeObject(serializableObj); try (final SerializableEntity httpentity = new SerializableEntity(serializableObj, null)) { Assertions.assertEquals(-1, httpentity.getContentLength()); Assertions.assertNotNull(httpentity.getContent()); Assertions.assertTrue(httpentity.isRepeatable()); Assertions.assertFalse(httpentity.isStreaming()); } } @Test public void testWriteTo() throws Exception { final Serializable serializableObj = new SerializableObject(); try (final SerializableEntity httpentity = new SerializableEntity(serializableObj, null)) { final ByteArrayOutputStream out = new ByteArrayOutputStream(); httpentity.writeTo(out); final byte[] bytes = out.toByteArray(); Assertions.assertNotNull(bytes); final ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(bytes)); final SerializableObject serIn = (SerializableObject) oin.readObject(); Assertions.assertEquals(4, serIn.intValue); Assertions.assertEquals("Hello", serIn.stringValue); Assertions.assertThrows(NullPointerException.class, () -> httpentity.writeTo(null)); } } } httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestFileEntity.java0100664 0000000 0000000 00000006664 14403631147 026332 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link FileEntity}. * */ public class TestFileEntity { @Test public void testBasics() throws Exception { final File tmpfile = File.createTempFile("testfile", ".txt"); tmpfile.deleteOnExit(); try (final FileEntity httpentity = new FileEntity(tmpfile, ContentType.TEXT_PLAIN)) { Assertions.assertEquals(tmpfile.length(), httpentity.getContentLength()); final InputStream content = httpentity.getContent(); Assertions.assertNotNull(content); content.close(); Assertions.assertTrue(httpentity.isRepeatable()); Assertions.assertFalse(httpentity.isStreaming()); Assertions.assertTrue(tmpfile.delete(), "Failed to delete " + tmpfile); } } @Test public void testNullConstructor() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> new FileEntity(null, ContentType.TEXT_PLAIN)); } @Test public void testWriteTo() throws Exception { final File tmpfile = File.createTempFile("testfile", ".txt"); tmpfile.deleteOnExit(); final FileOutputStream outStream = new FileOutputStream(tmpfile); outStream.write(0); outStream.write(1); outStream.write(2); outStream.write(3); outStream.close(); try (final FileEntity httpentity = new FileEntity(tmpfile, ContentType.TEXT_PLAIN)) { final ByteArrayOutputStream out = new ByteArrayOutputStream(); httpentity.writeTo(out); final byte[] bytes = out.toByteArray(); Assertions.assertNotNull(bytes); Assertions.assertEquals(tmpfile.length(), bytes.length); for (int i = 0; i < 4; i++) { Assertions.assertEquals(i, bytes[i]); } Assertions.assertTrue(tmpfile.delete(), "Failed to delete: " + tmpfile); Assertions.assertThrows(NullPointerException.class, () -> httpentity.writeTo(null)); } } } httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestByteArrayEntity.java0100664 0000000 0000000 00000013305 14403631147 027343 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link ByteArrayEntity}. * */ public class TestByteArrayEntity { @Test public void testBasics() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); try (final ByteArrayEntity entity = new ByteArrayEntity(bytes, null)) { Assertions.assertEquals(bytes.length, entity.getContentLength()); Assertions.assertNotNull(entity.getContent()); Assertions.assertTrue(entity.isRepeatable()); Assertions.assertFalse(entity.isStreaming()); } } @Test public void testBasicOffLen() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); try (final ByteArrayEntity entity = new ByteArrayEntity(bytes, 8, 7, null)) { Assertions.assertEquals(7, entity.getContentLength()); Assertions.assertNotNull(entity.getContent()); Assertions.assertTrue(entity.isRepeatable()); Assertions.assertFalse(entity.isStreaming()); } } @Test public void testIllegalConstructorNullByteArray() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> new ByteArrayEntity(null, null)); } @Test public void testIllegalConstructorBadLen() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); Assertions.assertThrows(IllegalArgumentException.class, () -> new ByteArrayEntity(bytes, 0, bytes.length + 1, null)); } @Test public void testIllegalConstructorBadOff1() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); Assertions.assertThrows(IllegalArgumentException.class, () -> new ByteArrayEntity(bytes, -1, bytes.length, null)); } @Test public void testIllegalConstructorBadOff2() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); Assertions.assertThrows(IllegalArgumentException.class, () -> new ByteArrayEntity(bytes, bytes.length + 1, bytes.length, null)); } @Test public void testWriteTo() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); try (final ByteArrayEntity entity = new ByteArrayEntity(bytes, null)) { ByteArrayOutputStream out = new ByteArrayOutputStream(); entity.writeTo(out); byte[] bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } out = new ByteArrayOutputStream(); entity.writeTo(out); bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } Assertions.assertThrows(NullPointerException.class, () -> entity.writeTo(null)); } } @Test public void testWriteToOffLen() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII); final int off = 8; final int len = 7; try (final ByteArrayEntity entity = new ByteArrayEntity(bytes, off, len, null)) { ByteArrayOutputStream out = new ByteArrayOutputStream(); entity.writeTo(out); byte[] bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(len, bytes2.length); for (int i = 0; i < len; i++) { Assertions.assertEquals(bytes[i + off], bytes2[i]); } out = new ByteArrayOutputStream(); entity.writeTo(out); bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(len, bytes2.length); for (int i = 0; i < len; i++) { Assertions.assertEquals(bytes[i + off], bytes2[i]); } Assertions.assertThrows(NullPointerException.class, () -> entity.writeTo(null)); } } } httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestHttpEntityWrapper.java0100664 0000000 0000000 00000007722 14435411677 027740 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link HttpEntityWrapper}. * */ public class TestHttpEntityWrapper { @Test public void testBasics() throws Exception { final StringEntity entity = new StringEntity("Message content", ContentType.TEXT_PLAIN, "blah", false); try (final HttpEntityWrapper wrapped = new HttpEntityWrapper(entity)) { Assertions.assertEquals(entity.getContentLength(), wrapped.getContentLength()); Assertions.assertEquals(entity.getContentType(), wrapped.getContentType()); Assertions.assertEquals(entity.getContentEncoding(), wrapped.getContentEncoding()); Assertions.assertEquals(entity.isChunked(), wrapped.isChunked()); Assertions.assertEquals(entity.isRepeatable(), wrapped.isRepeatable()); Assertions.assertEquals(entity.isStreaming(), wrapped.isStreaming()); Assertions.assertNotNull(wrapped.getContent()); } } @Test public void testIllegalConstructor() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> new HttpEntityWrapper(null)); } @Test public void testWriteTo() throws Exception { final String s = "Message content"; final byte[] bytes = s.getBytes(StandardCharsets.ISO_8859_1); final StringEntity entity = new StringEntity(s); try (final HttpEntityWrapper wrapped = new HttpEntityWrapper(entity)) { ByteArrayOutputStream out = new ByteArrayOutputStream(); wrapped.writeTo(out); byte[] bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } out = new ByteArrayOutputStream(); wrapped.writeTo(out); bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } Assertions.assertThrows(NullPointerException.class, () -> wrapped.writeTo(null)); } } @Test public void testConsumeContent() throws Exception { final String s = "Message content"; final StringEntity entity = new StringEntity(s); final HttpEntityWrapper wrapped = new HttpEntityWrapper(entity); EntityUtils.consume(wrapped); EntityUtils.consume(wrapped); } } httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestStringEntity.java0100664 0000000 0000000 00000012103 14435411677 026713 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayOutputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link StringEntity}. */ public class TestStringEntity { @Test public void testBasics() throws Exception { final String s = "Message content"; try (final StringEntity httpentity = new StringEntity(s, ContentType.TEXT_PLAIN)) { final byte[] bytes = s.getBytes(StandardCharsets.ISO_8859_1); Assertions.assertEquals(bytes.length, httpentity.getContentLength()); Assertions.assertNotNull(httpentity.getContent()); Assertions.assertTrue(httpentity.isRepeatable()); Assertions.assertFalse(httpentity.isStreaming()); } } @Test public void testNullConstructor() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> new StringEntity(null)); } @Test public void testDefaultContent() throws Exception { final String s = "Message content"; StringEntity httpentity = new StringEntity(s, ContentType.create("text/csv", "ANSI_X3.4-1968")); Assertions.assertEquals("text/csv; charset=US-ASCII", httpentity.getContentType()); httpentity = new StringEntity(s, StandardCharsets.US_ASCII); Assertions.assertEquals("text/plain; charset=US-ASCII", httpentity.getContentType()); httpentity = new StringEntity(s); Assertions.assertEquals("text/plain; charset=ISO-8859-1", httpentity.getContentType()); } private static String constructString(final int [] unicodeChars) { final StringBuilder buffer = new StringBuilder(); if (unicodeChars != null) { for (final int unicodeChar : unicodeChars) { buffer.append((char)unicodeChar); } } return buffer.toString(); } static final int SWISS_GERMAN_HELLO [] = { 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4 }; @Test public void testNullCharset() throws Exception { final String s = constructString(SWISS_GERMAN_HELLO); StringEntity httpentity = new StringEntity(s, ContentType.create("text/plain", (Charset) null)); Assertions.assertNotNull(httpentity.getContentType()); Assertions.assertEquals("text/plain", httpentity.getContentType()); Assertions.assertEquals(s, EntityUtils.toString(httpentity)); httpentity = new StringEntity(s, (Charset) null); Assertions.assertNotNull(httpentity.getContentType()); Assertions.assertEquals("text/plain", httpentity.getContentType()); Assertions.assertEquals(s, EntityUtils.toString(httpentity)); } @Test public void testWriteTo() throws Exception { final String s = "Message content"; final byte[] bytes = s.getBytes(StandardCharsets.ISO_8859_1); try (final StringEntity httpentity = new StringEntity(s)) { ByteArrayOutputStream out = new ByteArrayOutputStream(); httpentity.writeTo(out); byte[] bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } out = new ByteArrayOutputStream(); httpentity.writeTo(out); bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } Assertions.assertThrows(NullPointerException.class, () -> httpentity.writeTo(null)); } } } httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestPathEntity.java0100664 0000000 0000000 00000007543 14403631147 026344 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link PathEntity}. */ public class TestPathEntity { @Test public void testBasics() throws Exception { final Path tmpPath = Files.createTempFile("testfile", ".txt"); // Mark the file for deletion on VM exit if an assertion fails. tmpPath.toFile().deleteOnExit(); try (final PathEntity httpEntity = new PathEntity(tmpPath, ContentType.TEXT_PLAIN)) { Assertions.assertEquals(Files.size(tmpPath), httpEntity.getContentLength()); try (final InputStream content = httpEntity.getContent()) { Assertions.assertNotNull(content); } Assertions.assertTrue(httpEntity.isRepeatable()); Assertions.assertFalse(httpEntity.isStreaming()); // If we can't delete the file now, then the PathEntity or test is hanging on to a file handle. Assertions.assertTrue(Files.deleteIfExists(tmpPath), "Failed to delete " + tmpPath); } } @Test public void testNullConstructor() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> new PathEntity(null, ContentType.TEXT_PLAIN)); } @Test public void testWriteTo() throws Exception { final Path tmpPath = Files.createTempFile("testfile", ".txt"); // Mark the file for deletion on VM exit if an assertion fails. tmpPath.toFile().deleteOnExit(); try (final OutputStream outStream = Files.newOutputStream(tmpPath)) { outStream.write(0); outStream.write(1); outStream.write(2); outStream.write(3); } try (final PathEntity httpEntity = new PathEntity(tmpPath, ContentType.TEXT_PLAIN)) { final ByteArrayOutputStream out = new ByteArrayOutputStream(); httpEntity.writeTo(out); final byte[] bytes = out.toByteArray(); Assertions.assertNotNull(bytes); Assertions.assertEquals(Files.size(tmpPath), bytes.length); for (int i = 0; i < 4; i++) { Assertions.assertEquals(i, bytes[i]); } // If we can't delete the file now, then the PathEntity or test is hanging on to a file handle Assertions.assertTrue(Files.deleteIfExists(tmpPath), "Failed to delete " + tmpPath); Assertions.assertThrows(NullPointerException.class, () -> httpEntity.writeTo(null)); } } } httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestByteBufferEntity.java0100664 0000000 0000000 00000006274 14403631147 027505 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link ByteBufferEntity}. * */ public class TestByteBufferEntity { @Test public void testBasics() throws Exception { final ByteBuffer bytes = ByteBuffer.wrap("Message content".getBytes(StandardCharsets.US_ASCII)); try (final ByteBufferEntity httpentity = new ByteBufferEntity(bytes, null)) { Assertions.assertEquals(bytes.capacity(), httpentity.getContentLength()); Assertions.assertNotNull(httpentity.getContent()); Assertions.assertFalse(httpentity.isRepeatable()); Assertions.assertFalse(httpentity.isStreaming()); } } @Test public void testWriteTo() throws Exception { final ByteBuffer bytes = ByteBuffer.wrap("Message content".getBytes(StandardCharsets.US_ASCII)); try (final ByteBufferEntity httpentity = new ByteBufferEntity(bytes, null)) { ByteArrayOutputStream out = new ByteArrayOutputStream(); httpentity.writeTo(out); byte[] bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.capacity(), bytes2.length); bytes.position(0); for (int i = 0; i < bytes2.length; i++) { Assertions.assertEquals(bytes.get(i), bytes2[i]); } out = new ByteArrayOutputStream(); httpentity.writeTo(out); bytes2 = out.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.capacity(), bytes2.length); bytes.position(0); for (int i = 0; i < bytes.capacity(); i++) { Assertions.assertEquals(bytes.get(i), bytes2[i]); } Assertions.assertThrows(NullPointerException.class, () -> httpentity.writeTo(null)); } } } httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestEntityUtils.java0100664 0000000 0000000 00000031751 14435411677 026557 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.io.entity; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.net.WWWFormCodec; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link EntityUtils}. * */ public class TestEntityUtils { @Test public void testNullEntityToByteArray() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> EntityUtils.toByteArray(null)); } @Test public void testMaxIntContentToByteArray() throws Exception { final byte[] content = "Message content".getBytes(StandardCharsets.ISO_8859_1); final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(content), Integer.MAX_VALUE + 100L, ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1)); Assertions.assertThrows(IllegalArgumentException.class, () -> EntityUtils.toByteArray(entity)); } @Test public void testUnknownLengthContentToByteArray() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.ISO_8859_1); final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), -1, null); final byte[] bytes2 = EntityUtils.toByteArray(entity); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } } @Test public void testKnownLengthContentToByteArray() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.ISO_8859_1); final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), bytes.length, null); final byte[] bytes2 = EntityUtils.toByteArray(entity); Assertions.assertNotNull(bytes2); Assertions.assertEquals(bytes.length, bytes2.length); for (int i = 0; i < bytes.length; i++) { Assertions.assertEquals(bytes[i], bytes2[i]); } } @Test public void testNullEntityToString() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> EntityUtils.toString(null)); } @Test public void testMaxIntContentToString() throws Exception { final byte[] content = "Message content".getBytes(StandardCharsets.ISO_8859_1); final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(content), Integer.MAX_VALUE + 100L, ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1)); Assertions.assertThrows(IllegalArgumentException.class, () -> EntityUtils.toString(entity)); } @Test public void testUnknownLengthContentToString() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.ISO_8859_1); final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), -1, null); final String s = EntityUtils.toString(entity, "ISO-8859-1"); Assertions.assertEquals("Message content", s); } @Test public void testKnownLengthContentToString() throws Exception { final byte[] bytes = "Message content".getBytes(StandardCharsets.ISO_8859_1); final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), bytes.length, ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1)); final String s = EntityUtils.toString(entity, StandardCharsets.ISO_8859_1); Assertions.assertEquals("Message content", s); } static final int SWISS_GERMAN_HELLO [] = { 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4 }; static final int RUSSIAN_HELLO [] = { 0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438, 0x432, 0x435, 0x442 }; private static String constructString(final int [] unicodeChars) { final StringBuilder buffer = new StringBuilder(); if (unicodeChars != null) { for (final int unicodeChar : unicodeChars) { buffer.append((char)unicodeChar); } } return buffer.toString(); } @Test public void testNoCharsetContentToString() throws Exception { final String content = constructString(SWISS_GERMAN_HELLO); final byte[] bytes = content.getBytes(StandardCharsets.ISO_8859_1); final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), ContentType.TEXT_PLAIN); final String s = EntityUtils.toString(entity); } @Test public void testDefaultCharsetContentToString() throws Exception { final String content = constructString(RUSSIAN_HELLO); final byte[] bytes = content.getBytes(Charset.forName("KOI8-R")); final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), ContentType.parse("text/plain")); final String s = EntityUtils.toString(entity, Charset.forName("KOI8-R")); Assertions.assertEquals(content, s); } @Test public void testContentWithContentTypeToString() throws Exception { final String content = constructString(RUSSIAN_HELLO); final byte[] bytes = content.getBytes(StandardCharsets.UTF_8); final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8)); final String s = EntityUtils.toString(entity, "ISO-8859-1"); Assertions.assertEquals(content, s); } @Test public void testContentWithInvalidContentTypeToString() throws Exception { final String content = constructString(RUSSIAN_HELLO); final byte[] bytes = content.getBytes(StandardCharsets.UTF_8); final HttpEntity entity = new AbstractHttpEntity("text/plain; charset=nosuchcharset", null) { @Override public InputStream getContent() throws IOException, UnsupportedOperationException { return new ByteArrayInputStream(bytes); } @Override public boolean isStreaming() { return false; } @Override public long getContentLength() { return bytes.length; } @Override public void close() throws IOException { } }; final String s = EntityUtils.toString(entity, "UTF-8"); Assertions.assertEquals(content, s); } private static void assertNameValuePair ( final NameValuePair parameter, final String expectedName, final String expectedValue) { Assertions.assertEquals(parameter.getName(), expectedName); Assertions.assertEquals(parameter.getValue(), expectedValue); } @Test public void testParseEntity() throws Exception { final StringEntity entity1 = new StringEntity("Name1=Value1", ContentType.APPLICATION_FORM_URLENCODED); final List result = EntityUtils.parse(entity1); Assertions.assertEquals(1, result.size()); assertNameValuePair(result.get(0), "Name1", "Value1"); final StringEntity entity2 = new StringEntity("Name1=Value1", ContentType.parse("text/test")); Assertions.assertTrue(EntityUtils.parse(entity2).isEmpty()); } @Test public void testParseUTF8Entity() throws Exception { final String ru_hello = constructString(RUSSIAN_HELLO); final String ch_hello = constructString(SWISS_GERMAN_HELLO); final List parameters = new ArrayList<>(); parameters.add(new BasicNameValuePair("russian", ru_hello)); parameters.add(new BasicNameValuePair("swiss", ch_hello)); final String s = WWWFormCodec.format(parameters, StandardCharsets.UTF_8); Assertions.assertEquals("russian=%D0%92%D1%81%D0%B5%D0%BC_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82" + "&swiss=Gr%C3%BCezi_z%C3%A4m%C3%A4", s); final StringEntity entity = new StringEntity(s, ContentType.APPLICATION_FORM_URLENCODED.withCharset(StandardCharsets.UTF_8)); final List result = EntityUtils.parse(entity); Assertions.assertEquals(2, result.size()); assertNameValuePair(result.get(0), "russian", ru_hello); assertNameValuePair(result.get(1), "swiss", ch_hello); } @Test public void testByteArrayMaxResultLength() throws IOException { final byte[] allBytes = "Message content".getBytes(StandardCharsets.ISO_8859_1); final Map testCases = new HashMap<>(); testCases.put(0, new byte[]{}); testCases.put(2, Arrays.copyOfRange(allBytes, 0, 2)); testCases.put(allBytes.length, allBytes); testCases.put(Integer.MAX_VALUE, allBytes); for (final Map.Entry tc : testCases.entrySet()) { final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(allBytes), allBytes.length, null); final byte[] bytes = EntityUtils.toByteArray(entity, tc.getKey()); final byte[] expectedBytes = tc.getValue(); Assertions.assertNotNull(bytes); Assertions.assertEquals(expectedBytes.length, bytes.length); for (int i = 0; i < expectedBytes.length; i++) { Assertions.assertEquals(expectedBytes[i], bytes[i]); } } } @Test public void testByteArrayMaxResultLengthWithNoContentLength() throws IOException { final byte b = 'b'; final byte[] allBytes = new byte[5000]; Arrays.fill(allBytes, b); final Map testCases = new HashMap<>(); testCases.put(0, new byte[]{}); testCases.put(2, Arrays.copyOfRange(allBytes, 0, 2)); testCases.put(allBytes.length, allBytes); testCases.put(Integer.MAX_VALUE, allBytes); for (final Map.Entry tc : testCases.entrySet()) { final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(allBytes), null); final byte[] bytes = EntityUtils.toByteArray(entity, tc.getKey()); final byte[] expectedBytes = tc.getValue(); Assertions.assertNotNull(bytes); Assertions.assertEquals(expectedBytes.length, bytes.length); for (int i = 0; i < expectedBytes.length; i++) { Assertions.assertEquals(expectedBytes[i], bytes[i]); } } } @Test public void testStringMaxResultLength() throws IOException, ParseException { final String allMessage = "Message content"; final byte[] allBytes = allMessage.getBytes(StandardCharsets.ISO_8859_1); final Map testCases = new HashMap<>(); testCases.put(7, allMessage.substring(0, 7)); testCases.put(allMessage.length(), allMessage); testCases.put(Integer.MAX_VALUE, allMessage); for (final Map.Entry tc : testCases.entrySet()) { final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(allBytes), allBytes.length, null); final String string = EntityUtils.toString(entity, StandardCharsets.ISO_8859_1, tc.getKey()); final String expectedString = tc.getValue(); Assertions.assertNotNull(string); Assertions.assertEquals(expectedString, string); } } } httpcore5/src/test/java/org/apache/hc/core5/http/TestProtocolVersion.java0100664 0000000 0000000 00000003646 14403631147 025477 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestProtocolVersion { private static final ProtocolVersion PROTOCOL_VERSION_0_0 = new ProtocolVersion("a", 0, 0); private static final ProtocolVersion PROTOCOL_VERSION_1_0 = new ProtocolVersion("b", 1, 0); private static final ProtocolVersion PROTOCOL_VERSION_1_2 = new ProtocolVersion("c", 1, 2); @Test public void testEqualsMajorMinor() { Assertions.assertTrue(PROTOCOL_VERSION_0_0.equals(0, 0)); Assertions.assertTrue(PROTOCOL_VERSION_1_0.equals(1, 0)); Assertions.assertTrue(PROTOCOL_VERSION_1_2.equals(1, 2)); // Assertions.assertFalse(PROTOCOL_VERSION_1_2.equals(2, 0)); } } httpcore5/src/test/java/org/apache/hc/core5/http/TestContentType.java0100664 0000000 0000000 00000022355 14435411723 024603 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.nio.charset.StandardCharsets; import java.util.Arrays; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link ContentType}. * */ public class TestContentType { @Test public void testBasis() throws Exception { final ContentType contentType = ContentType.create("text/plain", "US-ASCII"); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.US_ASCII, contentType.getCharset()); Assertions.assertEquals("text/plain; charset=US-ASCII", contentType.toString()); } @Test public void testWithCharset() throws Exception { ContentType contentType = ContentType.create("text/plain", "US-ASCII"); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.US_ASCII, contentType.getCharset()); Assertions.assertEquals("text/plain; charset=US-ASCII", contentType.toString()); contentType = contentType.withCharset(StandardCharsets.UTF_8); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals("UTF-8", contentType.getCharset().name()); Assertions.assertEquals("text/plain; charset=UTF-8", contentType.toString()); } @Test public void testWithCharsetString() throws Exception { ContentType contentType = ContentType.create("text/plain", "US-ASCII"); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.US_ASCII, contentType.getCharset()); Assertions.assertEquals("text/plain; charset=US-ASCII", contentType.toString()); contentType = contentType.withCharset(StandardCharsets.UTF_8); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.UTF_8, contentType.getCharset()); Assertions.assertEquals("text/plain; charset=UTF-8", contentType.toString()); } @Test public void testLowCaseText() throws Exception { final ContentType contentType = ContentType.create("Text/Plain", "ascii"); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.US_ASCII, contentType.getCharset()); } @Test public void testCreateInvalidInput() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> ContentType.create(null, (String) null)); Assertions.assertThrows(IllegalArgumentException.class, () -> ContentType.create(" ", (String) null)); Assertions.assertThrows(IllegalArgumentException.class, () -> ContentType.create("stuff;", (String) null)); Assertions.assertThrows(IllegalArgumentException.class, () -> ContentType.create("text/plain", ",")); } @Test public void testParse() throws Exception { final ContentType contentType = ContentType.parse("text/plain; charset=\"ascii\""); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.US_ASCII, contentType.getCharset()); Assertions.assertEquals("text/plain; charset=ascii", contentType.toString()); } @Test public void testParseMultiparam() throws Exception { final ContentType contentType = ContentType.parse("text/plain; charset=\"ascii\"; " + "p0 ; p1 = \"blah-blah\" ; p2 = \" yada yada \" "); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.US_ASCII, contentType.getCharset()); Assertions.assertEquals("text/plain; charset=ascii; p0; p1=blah-blah; p2=\" yada yada \"", contentType.toString()); Assertions.assertNull(contentType.getParameter("p0")); Assertions.assertEquals("blah-blah", contentType.getParameter("p1")); Assertions.assertEquals(" yada yada ", contentType.getParameter("p2")); Assertions.assertNull(contentType.getParameter("p3")); } @Test public void testParseEmptyCharset() throws Exception { final ContentType contentType = ContentType.parse("text/plain; charset=\" \""); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertNull(contentType.getCharset()); } @Test public void testParseDefaultCharset() throws Exception { final ContentType contentType = ContentType.parse("text/plain; charset=\" \""); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertNull(contentType.getCharset()); Assertions.assertEquals(StandardCharsets.US_ASCII, contentType.getCharset(StandardCharsets.US_ASCII)); Assertions.assertNull(contentType.getCharset(null)); // Assertions.assertNull(ContentType.getCharset(contentType, null)); Assertions.assertEquals(StandardCharsets.US_ASCII, ContentType.getCharset(contentType, StandardCharsets.US_ASCII)); } @Test public void testParseEmptyValue() throws Exception { Assertions.assertNull(ContentType.parse(null)); Assertions.assertNull(ContentType.parse("")); Assertions.assertNull(ContentType.parse(" ")); Assertions.assertNull(ContentType.parse(";")); Assertions.assertNull(ContentType.parse("=")); } @Test public void testWithParamArrayChange() throws Exception { final BasicNameValuePair[] params = {new BasicNameValuePair("charset", "UTF-8"), new BasicNameValuePair("p", "this"), new BasicNameValuePair("p", "that")}; final ContentType contentType = ContentType.create("text/plain", params); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.UTF_8, contentType.getCharset()); Assertions.assertEquals("text/plain; charset=UTF-8; p=this; p=that", contentType.toString()); Arrays.setAll(params, i -> null); Assertions.assertEquals("this", contentType.getParameter("p")); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.UTF_8, contentType.getCharset()); Assertions.assertEquals("text/plain; charset=UTF-8; p=this; p=that", contentType.toString()); } @Test public void testWithParams() throws Exception { ContentType contentType = ContentType.create("text/plain", new BasicNameValuePair("charset", "UTF-8"), new BasicNameValuePair("p", "this"), new BasicNameValuePair("p", "that")); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.UTF_8, contentType.getCharset()); Assertions.assertEquals("text/plain; charset=UTF-8; p=this; p=that", contentType.toString()); contentType = contentType.withParameters( new BasicNameValuePair("charset", "ascii"), new BasicNameValuePair("p", "this and that")); Assertions.assertEquals("text/plain", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.US_ASCII, contentType.getCharset()); Assertions.assertEquals("text/plain; charset=ascii; p=\"this and that\"", contentType.toString()); contentType = ContentType.create("text/blah").withParameters( new BasicNameValuePair("p", "blah")); Assertions.assertEquals("text/blah", contentType.getMimeType()); Assertions.assertNull(contentType.getCharset()); Assertions.assertEquals("text/blah; p=blah", contentType.toString()); contentType = ContentType.create("text/blah", StandardCharsets.ISO_8859_1).withParameters( new BasicNameValuePair("p", "blah")); Assertions.assertEquals("text/blah", contentType.getMimeType()); Assertions.assertEquals(StandardCharsets.ISO_8859_1, contentType.getCharset()); Assertions.assertEquals("text/blah; charset=ISO-8859-1; p=blah", contentType.toString()); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/0040775 0000000 0000000 00000000000 14403631147 021560 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/impl/TestDefaultContentLengthStrategy.java0100664 0000000 0000000 00000011747 14403631147 031076 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.NotImplementedException; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.HeaderGroup; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDefaultContentLengthStrategy { static class TestHttpMessage extends HeaderGroup implements HttpMessage { private static final long serialVersionUID = 1L; @Override public ProtocolVersion getVersion() { return null; } @Override public void addHeader(final String name, final Object value) { addHeader(new BasicHeader(name, value)); } @Override public void setHeader(final String name, final Object value) { setHeader(new BasicHeader(name, value)); } @Override public void setVersion(final ProtocolVersion version) { } } @Test public void testEntityWithChunkTransferEncoding() throws Exception { final ContentLengthStrategy lenStrategy = new DefaultContentLengthStrategy(); final HttpMessage message = new TestHttpMessage(); message.addHeader("Transfer-Encoding", "Chunked"); Assertions.assertEquals(ContentLengthStrategy.CHUNKED, lenStrategy.determineLength(message)); } @Test public void testEntityWithIdentityTransferEncoding() throws Exception { final ContentLengthStrategy lenStrategy = new DefaultContentLengthStrategy(); final HttpMessage message = new TestHttpMessage(); message.addHeader("Transfer-Encoding", "Identity"); Assertions.assertThrows(NotImplementedException.class, () -> lenStrategy.determineLength(message)); } @Test public void testEntityWithInvalidTransferEncoding() throws Exception { final ContentLengthStrategy lenStrategy = new DefaultContentLengthStrategy(); final HttpMessage message = new TestHttpMessage(); message.addHeader("Transfer-Encoding", "whatever"); Assertions.assertThrows(ProtocolException.class, () -> lenStrategy.determineLength(message)); } @Test public void testEntityWithContentLength() throws Exception { final ContentLengthStrategy lenStrategy = new DefaultContentLengthStrategy(); final HttpMessage message = new TestHttpMessage(); message.addHeader("Content-Length", "100"); Assertions.assertEquals(100, lenStrategy.determineLength(message)); } @Test public void testEntityWithInvalidContentLength() throws Exception { final ContentLengthStrategy lenStrategy = new DefaultContentLengthStrategy(); final HttpMessage message = new TestHttpMessage(); message.addHeader("Content-Length", "whatever"); Assertions.assertThrows(ProtocolException.class, () -> lenStrategy.determineLength(message)); } @Test public void testEntityWithNegativeContentLength() throws Exception { final ContentLengthStrategy lenStrategy = new DefaultContentLengthStrategy(); final HttpMessage message = new TestHttpMessage(); message.addHeader("Content-Length", "-10"); Assertions.assertThrows(ProtocolException.class, () -> lenStrategy.determineLength(message)); } @Test public void testEntityNoContentDelimiter() throws Exception { final ContentLengthStrategy lenStrategy = new DefaultContentLengthStrategy(); final HttpMessage message = new TestHttpMessage(); Assertions.assertEquals(ContentLengthStrategy.UNDEFINED, lenStrategy.determineLength(message)); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/IncomingEntityDetailsTest.java0100664 0000000 0000000 00000007644 14403631147 027541 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.MessageHeaders; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.HeaderGroup; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class IncomingEntityDetailsTest { @Test public void getContentLengthEmpty() { final MessageHeaders messageHeaders = new HeaderGroup(); final IncomingEntityDetails incomingEntityDetails = new IncomingEntityDetails(messageHeaders); assertAll( () -> assertEquals(-1, incomingEntityDetails.getContentLength()), () -> assertNull(incomingEntityDetails.getContentType()), () -> assertNull(incomingEntityDetails.getContentEncoding()), () -> assertEquals(incomingEntityDetails.getTrailerNames().size(), 0) ); } @Test public void messageHeadersNull() { Assertions.assertThrows(NullPointerException.class, () -> new IncomingEntityDetails(null), "Message Header Null"); } @Test public void getContentLength() { final MessageHeaders messageHeaders = new HeaderGroup(); final HeaderGroup headerGroup = new HeaderGroup(); final Header header = new BasicHeader("name", "value"); headerGroup.addHeader(header); final IncomingEntityDetails incomingEntityDetails = new IncomingEntityDetails(messageHeaders); assertAll( () -> assertEquals(-1, incomingEntityDetails.getContentLength()), () -> assertTrue(incomingEntityDetails.isChunked()) ); } @Test public void getTrailerNames() { final HeaderGroup messageHeaders = new HeaderGroup(); final Header header = new BasicHeader(HttpHeaders.TRAILER, "a, b, c, c"); messageHeaders.setHeaders(header); final IncomingEntityDetails incomingEntityDetails = new IncomingEntityDetails(messageHeaders); final Set incomingSet = incomingEntityDetails.getTrailerNames(); assertAll( () -> assertFalse(incomingSet.isEmpty()), () -> assertTrue(incomingSet.containsAll(Stream.of("a", "b", "c") .collect(Collectors.toCollection(HashSet::new)))) ); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/0040775 0000000 0000000 00000000000 14403631147 022345 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestChunkEncoder.java0100664 0000000 0000000 00000023217 14403631147 026422 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import org.apache.hc.core5.http.WritableByteChannelMock; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; /** * Simple tests for {@link ChunkEncoder}. */ public class TestChunkEncoder { @Test public void testBasicCoding() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkEncoder encoder = new ChunkEncoder(channel, outbuf, metrics); encoder.write(CodecTestUtils.wrap("12345")); encoder.write(CodecTestUtils.wrap("678")); encoder.write(CodecTestUtils.wrap("90")); encoder.complete(); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("5\r\n12345\r\n3\r\n678\r\n2\r\n90\r\n0\r\n\r\n", s); Assertions.assertEquals("[chunk-coded; completed: true]", encoder.toString()); } @Test public void testChunkNoExceed() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkEncoder encoder = new ChunkEncoder(channel, outbuf, metrics); encoder.write(CodecTestUtils.wrap("1234")); encoder.complete(); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("4\r\n1234\r\n0\r\n\r\n", s); } @Test // See HTTPCORE-239 public void testLimitedChannel() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(16, 16); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(16, 16); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkEncoder encoder = new ChunkEncoder(channel, outbuf, metrics); // fill up the channel channel.write(CodecTestUtils.wrap("0123456789ABCDEF")); // fill up the out buffer outbuf.write(CodecTestUtils.wrap("0123456789ABCDEF")); final ByteBuffer src = CodecTestUtils.wrap("0123456789ABCDEF"); Assertions.assertEquals(0, encoder.write(src)); Assertions.assertEquals(0, encoder.write(src)); Assertions.assertEquals(0, encoder.write(src)); // should not be able to copy any bytes, until we flush the channel and buffer channel.reset(); outbuf.flush(channel); channel.reset(); Assertions.assertEquals(10, encoder.write(src)); channel.flush(); Assertions.assertEquals(6, encoder.write(src)); channel.flush(); Assertions.assertEquals(0, encoder.write(src)); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("4\r\n0123\r\n4\r\n4567\r\n2\r\n89\r\n4\r\nABCD\r\n2\r\nEF\r\n", s); } @Test public void testBufferFragments() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(1024)); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 1024); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkEncoder encoder = new ChunkEncoder(channel, outbuf, metrics, 1024); Assertions.assertEquals(16, encoder.write(CodecTestUtils.wrap("0123456789ABCDEF"))); Assertions.assertEquals(16, encoder.write(CodecTestUtils.wrap("0123456789ABCDEF"))); Assertions.assertEquals(16, encoder.write(CodecTestUtils.wrap("0123456789ABCDEF"))); Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("10\r\n0123456789ABCDEF\r\n10\r\n0123456789ABCDEF\r\n" + "10\r\n0123456789ABCDEF\r\n", s); } @Test public void testChunkExceed() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(16, 16); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkEncoder encoder = new ChunkEncoder(channel, outbuf, metrics); final ByteBuffer src = CodecTestUtils.wrap("0123456789ABCDEF"); Assertions.assertEquals(16, encoder.write(src)); Assertions.assertEquals(0, src.remaining()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("4\r\n0123\r\n4\r\n4567\r\n4\r\n89AB\r\n4\r\nCDEF\r\n", s); } @Test public void testCodingEmptyBuffer() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkEncoder encoder = new ChunkEncoder(channel, outbuf, metrics); encoder.write(CodecTestUtils.wrap("12345")); encoder.write(CodecTestUtils.wrap("678")); encoder.write(CodecTestUtils.wrap("90")); final ByteBuffer empty = ByteBuffer.allocate(100); empty.flip(); encoder.write(empty); encoder.write(null); encoder.complete(); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("5\r\n12345\r\n3\r\n678\r\n2\r\n90\r\n0\r\n\r\n", s); } @Test public void testCodingCompleted() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkEncoder encoder = new ChunkEncoder(channel, outbuf, metrics); encoder.write(CodecTestUtils.wrap("12345")); encoder.write(CodecTestUtils.wrap("678")); encoder.write(CodecTestUtils.wrap("90")); encoder.complete(); Assertions.assertThrows(IllegalStateException.class, () -> encoder.write(CodecTestUtils.wrap("more stuff"))); Assertions.assertThrows(IllegalStateException.class, () -> encoder.complete()); } @Test public void testInvalidConstructor() { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); Assertions.assertThrows(NullPointerException.class, () -> new ChunkEncoder(null, null, null)); Assertions.assertThrows(NullPointerException.class, () -> new ChunkEncoder(channel, null, null)); Assertions.assertThrows(NullPointerException.class, () -> new ChunkEncoder(channel, outbuf, null)); } @Test public void testTrailers() throws IOException { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkEncoder encoder = new ChunkEncoder(channel, outbuf, metrics, 0); encoder.write(CodecTestUtils.wrap("1")); encoder.write(CodecTestUtils.wrap("23")); encoder.complete(Arrays.asList(new BasicHeader("E", ""), new BasicHeader("Y", "Z"))); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("1\r\n1\r\n2\r\n23\r\n0\r\nE: \r\nY: Z\r\n\r\n", s); Assertions.assertEquals("[chunk-coded; completed: true]", encoder.toString()); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestIdentityEncoder.java0100664 0000000 0000000 00000061040 14403631147 027137 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.WritableByteChannelMock; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; /** * Simple tests for {@link IdentityEncoder}. */ public class TestIdentityEncoder { private File tmpfile; protected File createTempFile() throws IOException { this.tmpfile = File.createTempFile("testFile", ".txt"); return this.tmpfile; } @AfterEach public void deleteTempFile() { if (this.tmpfile != null && this.tmpfile.exists()) { this.tmpfile.delete(); } } @Test public void testBasicCoding() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); encoder.complete(); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals(5, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff", s); Assertions.assertEquals("[identity; completed: true]", encoder.toString()); } @Test public void testCodingEmptySrcBuffer() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics); encoder.write(CodecTestUtils.wrap("stuff")); final ByteBuffer empty = ByteBuffer.allocate(100); empty.flip(); encoder.write(empty); encoder.write(null); encoder.complete(); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("stuff", s); } @Test public void testCodingCompleted() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics); encoder.write(CodecTestUtils.wrap("stuff")); encoder.complete(); Assertions.assertThrows(IllegalStateException.class, () -> encoder.write(CodecTestUtils.wrap("more stuff"))); } @Test public void testInvalidConstructor() { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); Assertions.assertThrows(NullPointerException.class, () -> new IdentityEncoder(null, null, null)); Assertions.assertThrows(NullPointerException.class, () -> new IdentityEncoder(channel, null, null)); Assertions.assertThrows(NullPointerException.class, () -> new IdentityEncoder(channel, outbuf, null)); } @Test public void testCodingFromFile() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics); createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); try { testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII)); testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII)); } finally { testfile.close(); } testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); encoder.transfer(fchannel, 0, 20); } finally { testfile.close(); } final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertFalse(encoder.isCompleted()); Assertions.assertEquals("stuff;more stuff", s); } @Test public void testCodingEmptyFile() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics); encoder.write(CodecTestUtils.wrap("stuff;")); //Create an empty file createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); testfile.close(); testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); encoder.transfer(fchannel, 0, 20); encoder.write(CodecTestUtils.wrap("more stuff")); } finally { testfile.close(); } final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertFalse(encoder.isCompleted()); Assertions.assertEquals("stuff;more stuff", s); } @Test public void testCodingFromFileSmaller() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics); createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); try { testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII)); testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII)); } finally { testfile.close(); } testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); encoder.transfer(fchannel, 0, 20); } finally { testfile.close(); } final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertFalse(encoder.isCompleted()); Assertions.assertEquals("stuff;more stuff", s); } @Test public void testCodingFromFileFlushBuffer() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append("header"); outbuf.writeLine(chbuffer); createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); try { testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII)); testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII)); } finally { testfile.close(); } testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); encoder.transfer(fchannel, 0, 20); } finally { testfile.close(); } final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertFalse(encoder.isCompleted()); Assertions.assertEquals("header\r\nstuff;more stuff", s); } @Test public void testCodingFromFileChannelSaturated() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64, 4); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append("header"); outbuf.writeLine(chbuffer); createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); try { testfile.write("stuff".getBytes(StandardCharsets.US_ASCII)); } finally { testfile.close(); } testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); encoder.transfer(fchannel, 0, 20); encoder.transfer(fchannel, 0, 20); } finally { testfile.close(); } final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertFalse(encoder.isCompleted()); Assertions.assertEquals("head", s); } @Test public void testCodingNoFragmentBuffering() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append("header"); outbuf.writeLine(chbuffer); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics, 0); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.never()).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).flush(channel); Assertions.assertEquals(13, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("header\r\nstuff", s); } @Test public void testCodingFragmentBuffering() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append("header"); outbuf.writeLine(chbuffer); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics, 32); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.never()).flush(channel); Assertions.assertEquals(0, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("header\r\nstuff", s); } @Test public void testCodingFragmentBufferingMultipleFragments() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics, 32); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff"))); Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.never()).flush(channel); Assertions.assertEquals(0, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff-more stuff", s); } @Test public void testCodingFragmentBufferingLargeFragment() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append("header"); outbuf.writeLine(chbuffer); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics, 2); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.never()).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).flush(channel); Assertions.assertEquals(13, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("header\r\nstuff", s); } @Test public void testCodingFragmentBufferingTinyFragments() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics, 1); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff"))); Mockito.verify(channel, Mockito.times(5)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).flush(channel); Assertions.assertEquals(18, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff---more stuff", s); } @Test public void testCodingFragmentBufferingTinyFragments2() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics, 2); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff"))); Mockito.verify(channel, Mockito.times(4)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(2)).flush(channel); Assertions.assertEquals(18, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff---more stuff", s); } @Test public void testCodingFragmentBufferingTinyFragments3() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics, 3); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(2, encoder.write(CodecTestUtils.wrap("--"))); Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff"))); Mockito.verify(channel, Mockito.times(4)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(5)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(2)).flush(channel); Assertions.assertEquals(21, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff------more stuff", s); } @Test public void testCodingFragmentBufferingBufferFlush() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics, 8); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(6, encoder.write(CodecTestUtils.wrap("-stuff"))); Mockito.verify(channel, Mockito.times(1)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).flush(channel); Assertions.assertEquals(8, metrics.getBytesTransferred()); Assertions.assertEquals(3, outbuf.length()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff-stuff", s); } @Test public void testCodingFragmentBufferingBufferFlush2() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics, 8); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(16, encoder.write(CodecTestUtils.wrap("-much more stuff"))); Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).flush(channel); Assertions.assertEquals(21, metrics.getBytesTransferred()); Assertions.assertEquals(0, outbuf.length()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff-much more stuff", s); } @Test public void testCodingFragmentBufferingChannelSaturated() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64, 8)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics, 3); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(0, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(0, encoder.write(CodecTestUtils.wrap("more stuff"))); Mockito.verify(channel, Mockito.times(5)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(6)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(4)).flush(channel); Assertions.assertEquals(8, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff---", s); Assertions.assertEquals(3, outbuf.length()); } @Test public void testCodingFragmentBufferingChannelSaturated2() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64, 8)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityEncoder encoder = new IdentityEncoder(channel, outbuf, metrics, 8); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("much more stuff"))); Mockito.verify(channel, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).flush(channel); Assertions.assertEquals(8, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff--m", s); Assertions.assertEquals(0, outbuf.length()); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestLengthDelimitedDecoder.java0100664 0000000 0000000 00000057740 14403631147 030412 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ReadableByteChannelMock; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for {@link LengthDelimitedDecoder}. */ public class TestLengthDelimitedDecoder { private File tmpfile; protected File createTempFile() throws IOException { this.tmpfile = File.createTempFile("testFile", ".txt"); return this.tmpfile; } @AfterEach public void deleteTempFile() { if (this.tmpfile != null && this.tmpfile.exists()) { this.tmpfile.delete(); } } @Test public void testBasicDecoding() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff;", "more stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 16); final ByteBuffer dst = ByteBuffer.allocate(1024); int bytesRead = decoder.read(dst); Assertions.assertEquals(6, bytesRead); Assertions.assertEquals("stuff;", CodecTestUtils.convert(dst)); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(6, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(10, bytesRead); Assertions.assertEquals("more stuff", CodecTestUtils.convert(dst)); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(16, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(16, metrics.getBytesTransferred()); Assertions.assertEquals("[content length: 16; pos: 16; completed: true]", decoder.toString()); } @Test public void testCodingBeyondContentLimit() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] { "stuff;", "more stuff; and a lot more stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 16); final ByteBuffer dst = ByteBuffer.allocate(1024); int bytesRead = decoder.read(dst); Assertions.assertEquals(6, bytesRead); Assertions.assertEquals("stuff;", CodecTestUtils.convert(dst)); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(6, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(10, bytesRead); Assertions.assertEquals("more stuff", CodecTestUtils.convert(dst)); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(16, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(16, metrics.getBytesTransferred()); } @Test public void testBasicDecodingSmallBuffer() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff;", "more stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 16); final ByteBuffer dst = ByteBuffer.allocate(4); int bytesRead = decoder.read(dst); Assertions.assertEquals(4, bytesRead); Assertions.assertEquals("stuf", CodecTestUtils.convert(dst)); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(4, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(2, bytesRead); Assertions.assertEquals("f;", CodecTestUtils.convert(dst)); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(6, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(4, bytesRead); Assertions.assertEquals("more", CodecTestUtils.convert(dst)); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(10, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(4, bytesRead); Assertions.assertEquals(" stu", CodecTestUtils.convert(dst)); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(14, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(2, bytesRead); Assertions.assertEquals("ff", CodecTestUtils.convert(dst)); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(16, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(16, metrics.getBytesTransferred()); } @Test public void testDecodingFromSessionBuffer1() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff;", "more stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); inbuf.fill(channel); Assertions.assertEquals(6, inbuf.length()); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 16); final ByteBuffer dst = ByteBuffer.allocate(1024); int bytesRead = decoder.read(dst); Assertions.assertEquals(6, bytesRead); Assertions.assertEquals("stuff;", CodecTestUtils.convert(dst)); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(0, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(10, bytesRead); Assertions.assertEquals("more stuff", CodecTestUtils.convert(dst)); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(10, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(10, metrics.getBytesTransferred()); } @Test public void testDecodingFromSessionBuffer2() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] { "stuff;", "more stuff; and a lot more stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); inbuf.fill(channel); inbuf.fill(channel); Assertions.assertEquals(38, inbuf.length()); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 16); final ByteBuffer dst = ByteBuffer.allocate(1024); int bytesRead = decoder.read(dst); Assertions.assertEquals(16, bytesRead); Assertions.assertEquals("stuff;more stuff", CodecTestUtils.convert(dst)); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(0, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(0, metrics.getBytesTransferred()); } /* ----------------- FileChannel Part testing --------------------------- */ @Test public void testBasicDecodingFile() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff; ", "more stuff; ", "a lot more stuff!!!"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 36); createTempFile(); try (RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) { final FileChannel fchannel = testfile.getChannel(); long pos = 0; while (!decoder.isCompleted()) { final long bytesRead = decoder.transfer(fchannel, pos, 10); if (bytesRead > 0) { pos += bytesRead; } } } Assertions.assertEquals(this.tmpfile.length(), metrics.getBytesTransferred()); Assertions.assertEquals("stuff; more stuff; a lot more stuff!", CodecTestUtils.readFromFile(this.tmpfile)); } @Test public void testDecodingFileWithBufferedSessionData() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff; ", "more stuff; ", "a lot more stuff!!!"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 36); final int i = inbuf.fill(channel); Assertions.assertEquals(7, i); createTempFile(); try (RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) { final FileChannel fchannel = testfile.getChannel(); long pos = 0; while (!decoder.isCompleted()) { final long bytesRead = decoder.transfer(fchannel, pos, 10); if (bytesRead > 0) { pos += bytesRead; } } } Assertions.assertEquals(this.tmpfile.length() - 7, metrics.getBytesTransferred()); Assertions.assertEquals("stuff; more stuff; a lot more stuff!", CodecTestUtils.readFromFile(this.tmpfile)); } @Test public void testDecodingFileWithOffsetAndBufferedSessionData() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff; ", "more stuff; ", "a lot more stuff!"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 36); final int i = inbuf.fill(channel); Assertions.assertEquals(7, i); final byte[] beginning = "beginning; ".getBytes(StandardCharsets.US_ASCII); createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); try { testfile.write(beginning); } finally { testfile.close(); } testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); long pos = beginning.length; while (!decoder.isCompleted()) { if(testfile.length() < pos) { testfile.setLength(pos); } final long bytesRead = decoder.transfer(fchannel, pos, 10); if (bytesRead > 0) { pos += bytesRead; } } } finally { testfile.close(); } // count everything except the initial 7 bytes that went to the session buffer Assertions.assertEquals(this.tmpfile.length() - 7 - beginning.length, metrics.getBytesTransferred()); Assertions.assertEquals("beginning; stuff; more stuff; a lot more stuff!", CodecTestUtils.readFromFile(this.tmpfile)); } @Test public void testDecodingFileWithLimit() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff; more stuff; ", "a lot more stuff!!!"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(channel, inbuf, metrics, 36); final int i = inbuf.fill(channel); Assertions.assertEquals(19, i); createTempFile(); try (RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) { final FileChannel fchannel = testfile.getChannel(); long pos = 0; // transferred from buffer long bytesRead = decoder.transfer(fchannel, pos, 1); Assertions.assertEquals(1, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(0, metrics.getBytesTransferred()); pos += bytesRead; bytesRead = decoder.transfer(fchannel, pos, 2); Assertions.assertEquals(2, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(0, metrics.getBytesTransferred()); pos += bytesRead; bytesRead = decoder.transfer(fchannel, pos, 17); Assertions.assertEquals(16, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(0, metrics.getBytesTransferred()); pos += bytesRead; // transferred from channel bytesRead = decoder.transfer(fchannel, pos, 1); Assertions.assertEquals(1, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(1, metrics.getBytesTransferred()); pos += bytesRead; bytesRead = decoder.transfer(fchannel, pos, 2); Assertions.assertEquals(2, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(3, metrics.getBytesTransferred()); pos += bytesRead; bytesRead = decoder.transfer(fchannel, pos, 15); Assertions.assertEquals(14, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(17, metrics.getBytesTransferred()); pos += bytesRead; bytesRead = decoder.transfer(fchannel, pos, 1); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(17, metrics.getBytesTransferred()); } Assertions.assertEquals("stuff; more stuff; a lot more stuff!", CodecTestUtils.readFromFile(this.tmpfile)); } @Test public void testWriteBeyondFileSize() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"a"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 1); createTempFile(); try (RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) { final FileChannel fchannel = testfile.getChannel(); Assertions.assertEquals(0, testfile.length()); Assertions.assertThrows(IOException.class, () -> decoder.transfer(fchannel, 5, 10)); } } @Test public void testCodingBeyondContentLimitFile() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] { "stuff;", "more stuff; and a lot more stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 16); createTempFile(); try (RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) { final FileChannel fchannel = testfile.getChannel(); long bytesRead = decoder.transfer(fchannel, 0, 6); Assertions.assertEquals(6, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(6, metrics.getBytesTransferred()); bytesRead = decoder.transfer(fchannel,0 , 10); Assertions.assertEquals(10, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(16, metrics.getBytesTransferred()); bytesRead = decoder.transfer(fchannel, 0, 1); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(16, metrics.getBytesTransferred()); } } @Test public void testInvalidConstructor() { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff;", "more stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); Assertions.assertThrows(NullPointerException.class, () -> new LengthDelimitedDecoder(null, null, null, 10)); Assertions.assertThrows(NullPointerException.class, () -> new LengthDelimitedDecoder(channel, null, null, 10)); Assertions.assertThrows(NullPointerException.class, () -> new LengthDelimitedDecoder(channel, inbuf, null, 10)); Assertions.assertThrows(IllegalArgumentException.class, () -> new LengthDelimitedDecoder(channel, inbuf, metrics, -10)); } @Test public void testInvalidInput() throws Exception { final String s = "stuff"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 3); Assertions.assertThrows(NullPointerException.class, () -> decoder.read(null)); } @Test public void testZeroLengthDecoding() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 0); final ByteBuffer dst = ByteBuffer.allocate(1024); final int bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(0, metrics.getBytesTransferred()); } @Test public void testTruncatedContent() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"1234567890"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 20); final ByteBuffer dst = ByteBuffer.allocate(1024); final int bytesRead = decoder.read(dst); Assertions.assertEquals(10, bytesRead); Assertions.assertThrows(ConnectionClosedException.class, () -> decoder.read(dst)); } @Test public void testTruncatedContentWithFile() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"1234567890"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder( channel, inbuf, metrics, 20); createTempFile(); try (RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) { final FileChannel fchannel = testfile.getChannel(); final long bytesRead = decoder.transfer(fchannel, 0, Integer.MAX_VALUE); Assertions.assertEquals(10, bytesRead); Assertions.assertThrows(ConnectionClosedException.class, () -> decoder.transfer(fchannel, 0, Integer.MAX_VALUE)); } } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestExpandableBuffer.java0100664 0000000 0000000 00000007045 14403631147 027250 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import static org.hamcrest.MatcherAssert.assertThat; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; public class TestExpandableBuffer { @Test public void testBasics() throws Exception { final ExpandableBuffer buffer = new ExpandableBuffer(16); assertThat(buffer.mode(), CoreMatchers.equalTo(ExpandableBuffer.Mode.INPUT)); assertThat(buffer.hasData(), CoreMatchers.equalTo(false)); buffer.setInputMode(); buffer.buffer().put(new byte[] { 0, 1, 2, 3, 4, 5}); assertThat(buffer.hasData(), CoreMatchers.equalTo(true)); assertThat(buffer.length(), CoreMatchers.equalTo(6)); assertThat(buffer.buffer().capacity(), CoreMatchers.equalTo(16)); assertThat(buffer.mode(), CoreMatchers.equalTo(ExpandableBuffer.Mode.OUTPUT)); buffer.setInputMode(); buffer.buffer().put(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); assertThat(buffer.length(), CoreMatchers.equalTo(16)); assertThat(buffer.buffer().capacity(), CoreMatchers.equalTo(16)); assertThat(buffer.mode(), CoreMatchers.equalTo(ExpandableBuffer.Mode.OUTPUT)); buffer.setInputMode(); buffer.ensureCapacity(22); buffer.buffer().put(new byte[] { 0, 1, 2, 3, 4, 5}); assertThat(buffer.length(), CoreMatchers.equalTo(22)); assertThat(buffer.buffer().capacity(), CoreMatchers.equalTo(22)); assertThat(buffer.mode(), CoreMatchers.equalTo(ExpandableBuffer.Mode.OUTPUT)); buffer.clear(); assertThat(buffer.mode(), CoreMatchers.equalTo(ExpandableBuffer.Mode.INPUT)); assertThat(buffer.hasData(), CoreMatchers.equalTo(false)); assertThat(buffer.capacity(), CoreMatchers.equalTo(22)); } @Test public void testAdjustCapacity() throws Exception { final ExpandableBuffer buffer = new ExpandableBuffer(16); assertThat(buffer.capacity(), CoreMatchers.equalTo(16)); buffer.ensureCapacity(21); assertThat(buffer.capacity(), CoreMatchers.equalTo(21)); buffer.ensureAdjustedCapacity(22); assertThat(buffer.capacity(), CoreMatchers.equalTo(1024)); buffer.ensureAdjustedCapacity(1024); assertThat(buffer.capacity(), CoreMatchers.equalTo(1024)); buffer.ensureAdjustedCapacity(1025); assertThat(buffer.capacity(), CoreMatchers.equalTo(2048)); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestChunkDecoder.java0100664 0000000 0000000 00000063465 14403631147 026421 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.charset.StandardCharsets; import java.util.List; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.MalformedChunkCodingException; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.ReadableByteChannelMock; import org.apache.hc.core5.http.TruncatedChunkException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for {@link ChunkDecoder}. */ public class TestChunkDecoder { @Test public void testBasicDecoding() throws Exception { final String s = "5\r\n01234\r\n5\r\n56789\r\n6\r\nabcdef\r\n0\r\n\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); int bytesRead = decoder.read(dst); Assertions.assertEquals(16, bytesRead); Assertions.assertEquals("0123456789abcdef", CodecTestUtils.convert(dst)); final List trailers = decoder.getTrailers(); Assertions.assertNull(trailers); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals("[chunk-coded; completed: true]", decoder.toString()); } @Test public void testComplexDecoding() throws Exception { final String s = "10;key=\"value\"\r\n1234567890123456\r\n" + "5\r\n12345\r\n5\r\n12345\r\n0\r\nFooter1: abcde\r\nFooter2: fghij\r\n\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); int bytesRead = 0; while (dst.hasRemaining() && !decoder.isCompleted()) { final int i = decoder.read(dst); if (i > 0) { bytesRead += i; } } Assertions.assertEquals(26, bytesRead); Assertions.assertEquals("12345678901234561234512345", CodecTestUtils.convert(dst)); final List trailers = decoder.getTrailers(); Assertions.assertEquals(2, trailers.size()); Assertions.assertEquals("Footer1", trailers.get(0).getName()); Assertions.assertEquals("abcde", trailers.get(0).getValue()); Assertions.assertEquals("Footer2", trailers.get(1).getName()); Assertions.assertEquals("fghij", trailers.get(1).getValue()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); } @Test public void testDecodingWithSmallBuffer() throws Exception { final String s1 = "5\r\n01234\r\n5\r\n5678"; final String s2 = "9\r\n6\r\nabcdef\r\n0\r\n\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s1, s2}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); final ByteBuffer tmp = ByteBuffer.allocate(4); int bytesRead = 0; while (dst.hasRemaining() && !decoder.isCompleted()) { final int i = decoder.read(tmp); if (i > 0) { bytesRead += i; } tmp.flip(); dst.put(tmp); tmp.compact(); } Assertions.assertEquals(16, bytesRead); Assertions.assertEquals("0123456789abcdef", CodecTestUtils.convert(dst)); Assertions.assertTrue(decoder.isCompleted()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); } @Test public void testMalformedChunk() throws Exception { final String s = "5\r\n01234----------------------------------------------------------" + "-----------------------------------------------------------------------------" + "-----------------------------------------------------------------------------"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(32, 32, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); Assertions.assertThrows(MalformedChunkCodingException.class, () -> decoder.read(dst)); } @Test public void testIncompleteChunkDecoding() throws Exception { final String[] chunks = { "10;", "key=\"value\"\r", "\n123456789012345", "6\r\n5\r\n12", "345\r\n6\r", "\nabcdef\r", "\n0\r\nFoot", "er1: abcde\r\nFooter2: f", "ghij\r\n\r\n" }; final ReadableByteChannel channel = new ReadableByteChannelMock( chunks, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ByteBuffer dst = ByteBuffer.allocate(1024); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); int bytesRead = 0; while (dst.hasRemaining() && !decoder.isCompleted()) { final int i = decoder.read(dst); if (i > 0) { bytesRead += i; } } Assertions.assertEquals(27, bytesRead); Assertions.assertEquals("123456789012345612345abcdef", CodecTestUtils.convert(dst)); Assertions.assertTrue(decoder.isCompleted()); final List trailers = decoder.getTrailers(); Assertions.assertEquals(2, trailers.size()); Assertions.assertEquals("Footer1", trailers.get(0).getName()); Assertions.assertEquals("abcde", trailers.get(0).getValue()); Assertions.assertEquals("Footer2", trailers.get(1).getName()); Assertions.assertEquals("fghij", trailers.get(1).getValue()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); } @Test public void testMalformedChunkSizeDecoding() throws Exception { final String s = "5\r\n01234\r\n5zz\r\n56789\r\n6\r\nabcdef\r\n0\r\n\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); Assertions.assertThrows(MalformedChunkCodingException.class, () -> decoder.read(dst)); } @Test public void testMalformedChunkEndingDecoding() throws Exception { final String s = "5\r\n01234\r\n5\r\n56789\r\r6\r\nabcdef\r\n0\r\n\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); Assertions.assertThrows(MalformedChunkCodingException.class, () -> decoder.read(dst)); } @Test public void testMalformedChunkTruncatedChunk() throws Exception { final String s = "3\r\n12"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); Assertions.assertEquals(2, decoder.read(dst)); Assertions.assertThrows(TruncatedChunkException.class, () -> decoder.read(dst)); } @Test public void testFoldedFooters() throws Exception { final String s = "10;key=\"value\"\r\n1234567890123456\r\n" + "5\r\n12345\r\n5\r\n12345\r\n0\r\nFooter1: abcde\r\n \r\n fghij\r\n\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); final int bytesRead = decoder.read(dst); Assertions.assertEquals(26, bytesRead); Assertions.assertEquals("12345678901234561234512345", CodecTestUtils.convert(dst)); final List trailers = decoder.getTrailers(); Assertions.assertEquals(1, trailers.size()); Assertions.assertEquals("Footer1", trailers.get(0).getName()); Assertions.assertEquals("abcde fghij", trailers.get(0).getValue()); } @Test public void testMalformedFooters() throws Exception { final String s = "10;key=\"value\"\r\n1234567890123456\r\n" + "5\r\n12345\r\n5\r\n12345\r\n0\r\nFooter1 abcde\r\n\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); Assertions.assertThrows(IOException.class, () -> decoder.read(dst)); } @Test public void testMissingLastCRLF() throws Exception { final String s = "10\r\n1234567890123456\r\n" + "5\r\n12345\r\n5\r\n12345"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); Assertions.assertThrows(MalformedChunkCodingException.class, () -> { while (dst.hasRemaining() && !decoder.isCompleted()) { decoder.read(dst); } }); } @Test public void testMissingClosingChunk() throws Exception { final String s = "10\r\n1234567890123456\r\n" + "5\r\n12345\r\n5\r\n12345\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); Assertions.assertThrows(ConnectionClosedException.class, () -> { long bytesRead = 0; try { while (dst.hasRemaining() && !decoder.isCompleted()) { final int i = decoder.read(dst); if (i > 0) { bytesRead += i; } } } catch (final MalformedChunkCodingException ex) { Assertions.assertEquals(26L, bytesRead); Assertions.assertEquals("12345678901234561234512345", CodecTestUtils.convert(dst)); Assertions.assertTrue(decoder.isCompleted()); throw ex; } }); } @Test public void testReadingWitSmallBuffer() throws Exception { final String s = "10\r\n1234567890123456\r\n" + "40\r\n12345678901234561234567890123456" + "12345678901234561234567890123456\r\n0\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); final ByteBuffer tmp = ByteBuffer.allocate(10); int bytesRead = 0; while (dst.hasRemaining() && !decoder.isCompleted()) { final int i = decoder.read(tmp); if (i > 0) { bytesRead += i; tmp.flip(); dst.put(tmp); tmp.compact(); } } Assertions.assertEquals(80, bytesRead); Assertions.assertEquals("12345678901234561234567890123456" + "12345678901234561234567890123456" + "1234567890123456", CodecTestUtils.convert(dst)); Assertions.assertTrue(decoder.isCompleted()); } @Test public void testEndOfStreamConditionReadingFooters() throws Exception { final String s = "10\r\n1234567890123456\r\n" + "5\r\n12345\r\n5\r\n12345\r\n0\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); int bytesRead = 0; while (dst.hasRemaining() && !decoder.isCompleted()) { final int i = decoder.read(dst); if (i > 0) { bytesRead += i; } } Assertions.assertEquals(26, bytesRead); Assertions.assertEquals("12345678901234561234512345", CodecTestUtils.convert(dst)); Assertions.assertTrue(decoder.isCompleted()); } @Test public void testTooLongChunkHeader() throws Exception { final String s = "5; and some very looooong comment\r\n12345\r\n0\r\n"; final ReadableByteChannel channel1 = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics1 = new BasicHttpTransportMetrics(); final SessionInputBuffer inbuf1 = new SessionInputBufferImpl(1024, 256); final ChunkDecoder decoder1 = new ChunkDecoder(channel1, inbuf1, metrics1); final ByteBuffer dst = ByteBuffer.allocate(1024); while (dst.hasRemaining() && !decoder1.isCompleted()) { decoder1.read(dst); } Assertions.assertEquals("12345", CodecTestUtils.convert(dst)); Assertions.assertTrue(decoder1.isCompleted()); final ReadableByteChannel channel2 = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf2 = new SessionInputBufferImpl(1024, 256, 10); final BasicHttpTransportMetrics metrics2 = new BasicHttpTransportMetrics(); final ChunkDecoder decoder2 = new ChunkDecoder(channel2, inbuf2, metrics2); dst.clear(); Assertions.assertThrows(MessageConstraintException.class, () -> decoder2.read(dst)); } @Test public void testTooLongFooter() throws Exception { final String s = "10\r\n1234567890123456\r\n" + "0\r\nFooter1: looooooooooooooooooooooooooooooooooooooooooooooooooooooog\r\n\r\n"; final ReadableByteChannel channel1 = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf1 = new SessionInputBufferImpl(1024, 256, 0); final BasicHttpTransportMetrics metrics1 = new BasicHttpTransportMetrics(); final ChunkDecoder decoder1 = new ChunkDecoder(channel1, inbuf1, metrics1); final ByteBuffer dst = ByteBuffer.allocate(1024); final int bytesRead = decoder1.read(dst); Assertions.assertEquals(16, bytesRead); Assertions.assertEquals("1234567890123456", CodecTestUtils.convert(dst)); final List trailers = decoder1.getTrailers(); Assertions.assertNotNull(trailers); Assertions.assertEquals(1, trailers.size()); final ReadableByteChannel channel2 = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf2 = new SessionInputBufferImpl(1024, 256, 25, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics2 = new BasicHttpTransportMetrics(); final ChunkDecoder decoder2 = new ChunkDecoder(channel2, inbuf2, metrics2); dst.clear(); Assertions.assertThrows(MessageConstraintException.class, () -> decoder2.read(dst)); } @Test public void testTooLongFoldedFooter() throws Exception { final String s = "10\r\n1234567890123456\r\n" + "0\r\nFooter1: blah\r\n blah\r\n blah\r\n blah\r\n blah\r\n blah\r\n blah\r\n blah\r\n\r\n"; final ReadableByteChannel channel1 = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf1 = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics1 = new BasicHttpTransportMetrics(); final ChunkDecoder decoder1 = new ChunkDecoder(channel1, inbuf1, metrics1); final ByteBuffer dst = ByteBuffer.allocate(1024); final int bytesRead = decoder1.read(dst); Assertions.assertEquals(16, bytesRead); Assertions.assertEquals("1234567890123456", CodecTestUtils.convert(dst)); final List trailers = decoder1.getTrailers(); Assertions.assertNotNull(trailers); Assertions.assertEquals(1, trailers.size()); final Http1Config http1Config = Http1Config.custom() .setMaxLineLength(25) .build(); final ReadableByteChannel channel2 = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf2 = new SessionInputBufferImpl(1024, 256,0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics2 = new BasicHttpTransportMetrics(); final ChunkDecoder decoder2 = new ChunkDecoder(channel2, inbuf2, http1Config, metrics2); dst.clear(); Assertions.assertThrows(MessageConstraintException.class, () -> decoder2.read(dst)); } @Test public void testTooManyFooters() throws Exception { final String s = "10\r\n1234567890123456\r\n" + "0\r\nFooter1: blah\r\nFooter2: blah\r\nFooter3: blah\r\nFooter4: blah\r\n\r\n"; final ReadableByteChannel channel1 = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf1 = new SessionInputBufferImpl(1024, 256,0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics1 = new BasicHttpTransportMetrics(); final ChunkDecoder decoder1 = new ChunkDecoder(channel1, inbuf1, metrics1); final ByteBuffer dst = ByteBuffer.allocate(1024); final int bytesRead = decoder1.read(dst); Assertions.assertEquals(16, bytesRead); Assertions.assertEquals("1234567890123456", CodecTestUtils.convert(dst)); final List trailers = decoder1.getTrailers(); Assertions.assertNotNull(trailers); Assertions.assertEquals(4, trailers.size()); final Http1Config http1Config = Http1Config.custom() .setMaxHeaderCount(3).build(); final ReadableByteChannel channel2 = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf2 = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics2 = new BasicHttpTransportMetrics(); final ChunkDecoder decoder2 = new ChunkDecoder(channel2, inbuf2, http1Config, metrics2); dst.clear(); Assertions.assertThrows(MessageConstraintException.class, () -> decoder2.read(dst)); } @Test public void testInvalidConstructor() { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff;", "more stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); Assertions.assertThrows(NullPointerException.class, () -> new ChunkDecoder(null, null, null)); Assertions.assertThrows(NullPointerException.class, () -> new ChunkDecoder(channel, inbuf, null)); } @Test public void testInvalidInput() throws Exception { final String s = "10;key=\"value\"\r\n1234567890123456\r\n" + "5\r\n12345\r\n5\r\n12345\r\n0\r\nFooter1 abcde\r\n\r\n"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); Assertions.assertThrows(NullPointerException.class, () -> decoder.read(null)); } @Test public void testHugeChunk() throws Exception { final String s = "1234567890abcdef\r\n0123456789abcdef"; final ReadableByteChannel channel = new ReadableByteChannelMock(new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final ChunkDecoder decoder = new ChunkDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(4); int bytesRead = decoder.read(dst); Assertions.assertEquals(4, bytesRead); Assertions.assertEquals("0123", CodecTestUtils.convert(dst)); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(4, bytesRead); Assertions.assertEquals("4567", CodecTestUtils.convert(dst)); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/ContentDecoderMock.java0100664 0000000 0000000 00000004223 14245617503 026724 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.nio.ContentDecoder; public class ContentDecoderMock implements ContentDecoder { private final ReadableByteChannel channel; private boolean completed; public ContentDecoderMock(final ReadableByteChannel channel) { super(); this.channel = channel; } @Override public int read(final ByteBuffer dst) throws IOException { if (this.completed) { return -1; } final int bytesRead = this.channel.read(dst); if (bytesRead == -1) { this.completed = true; } return bytesRead; } @Override public boolean isCompleted() { return this.completed; } @Override public List getTrailers() { return null; } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestLengthDelimitedEncoder.java0100664 0000000 0000000 00000070403 14403631147 030413 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.WritableByteChannelMock; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; /** * Simple tests for {@link LengthDelimitedEncoder}. */ public class TestLengthDelimitedEncoder { private File tmpfile; protected File createTempFile() throws IOException { this.tmpfile = File.createTempFile("testFile", ".txt"); return this.tmpfile; } @AfterEach public void deleteTempFile() { if (this.tmpfile != null && this.tmpfile.exists()) { this.tmpfile.delete(); } } @Test public void testBasicCoding() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder( channel, outbuf, metrics, 16); encoder.write(CodecTestUtils.wrap("stuff;")); encoder.write(CodecTestUtils.wrap("more stuff")); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("stuff;more stuff", s); Assertions.assertEquals("[content length: 16; pos: 16; completed: true]", encoder.toString()); } @Test public void testCodingBeyondContentLimit() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder( channel, outbuf, metrics, 16); encoder.write(CodecTestUtils.wrap("stuff;")); encoder.write(CodecTestUtils.wrap("more stuff; and a lot more stuff")); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("stuff;more stuff", s); } @Test public void testCodingEmptyBuffer() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder( channel, outbuf, metrics, 16); encoder.write(CodecTestUtils.wrap("stuff;")); final ByteBuffer empty = ByteBuffer.allocate(100); empty.flip(); encoder.write(empty); encoder.write(null); encoder.write(CodecTestUtils.wrap("more stuff")); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("stuff;more stuff", s); } @Test public void testCodingCompleted() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder( channel, outbuf, metrics, 5); encoder.write(CodecTestUtils.wrap("stuff")); Assertions.assertThrows(IllegalStateException.class, () -> encoder.write(CodecTestUtils.wrap("more stuff"))); } @Test public void testInvalidConstructor() { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); Assertions.assertThrows(NullPointerException.class, () -> new LengthDelimitedEncoder(null, null, null, 10)); Assertions.assertThrows(NullPointerException.class, () -> new LengthDelimitedEncoder(channel, null, null, 10)); Assertions.assertThrows(NullPointerException.class, () -> new LengthDelimitedEncoder(channel, outbuf, null, 10)); Assertions.assertThrows(IllegalArgumentException.class, () -> new LengthDelimitedEncoder(channel, outbuf, metrics, -10)); } @Test public void testCodingBeyondContentLimitFromFile() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder( channel, outbuf, metrics, 16); createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); try { testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII)); testfile.write("more stuff; and a lot more stuff".getBytes(StandardCharsets.US_ASCII)); } finally { testfile.close(); } testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); encoder.transfer(fchannel, 0, 20); } finally { testfile.close(); } final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("stuff;more stuff", s); } @Test public void testCodingEmptyFile() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder( channel, outbuf, metrics, 16); encoder.write(CodecTestUtils.wrap("stuff;")); //Create an empty file createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); testfile.close(); testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); encoder.transfer(fchannel, 0, 20); encoder.write(CodecTestUtils.wrap("more stuff")); } finally { testfile.close(); } final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("stuff;more stuff", s); } @Test public void testCodingCompletedFromFile() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder( channel, outbuf, metrics, 5); encoder.write(CodecTestUtils.wrap("stuff")); createTempFile(); try (final RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) { testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII)); } try (final RandomAccessFile file = new RandomAccessFile(this.tmpfile, "rw"); final FileChannel fchannel = file.getChannel()) { Assertions.assertThrows(IllegalStateException.class, () -> encoder.transfer(fchannel, 0, 10)); } } @Test public void testCodingFromFileSmaller() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder( channel, outbuf, metrics, 16); createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); try { testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII)); testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII)); } finally { testfile.close(); } testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); encoder.transfer(fchannel, 0, 20); } finally { testfile.close(); } final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("stuff;more stuff", s); } @Test public void testCodingFromFileFlushBuffer() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder( channel, outbuf, metrics, 16); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append("header"); outbuf.writeLine(chbuffer); createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); try { testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII)); testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII)); } finally { testfile.close(); } testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); encoder.transfer(fchannel, 0, 20); } finally { testfile.close(); } final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertTrue(encoder.isCompleted()); Assertions.assertEquals("header\r\nstuff;more stuff", s); } @Test public void testCodingFromFileChannelSaturated() throws Exception { final WritableByteChannelMock channel = new WritableByteChannelMock(64, 4); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder( channel, outbuf, metrics, 16); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append("header"); outbuf.writeLine(chbuffer); createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); try { testfile.write("stuff".getBytes(StandardCharsets.US_ASCII)); } finally { testfile.close(); } testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); encoder.transfer(fchannel, 0, 20); encoder.transfer(fchannel, 0, 20); } finally { testfile.close(); } final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertFalse(encoder.isCompleted()); Assertions.assertEquals("head", s); } @Test public void testCodingNoFragmentBuffering() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append("header"); outbuf.writeLine(chbuffer); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 100, 0); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.never()).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).flush(channel); Assertions.assertEquals(13, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("header\r\nstuff", s); } @Test public void testCodingFragmentBuffering() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append("header"); outbuf.writeLine(chbuffer); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 100, 32); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.never()).flush(channel); Assertions.assertEquals(0, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("header\r\nstuff", s); } @Test public void testCodingFragmentBufferingMultipleFragments() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 100, 32); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff"))); Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.never()).flush(channel); Assertions.assertEquals(0, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff-more stuff", s); } @Test public void testCodingFragmentBufferingMultipleFragmentsBeyondContentLimit() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 16, 32); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff; and a lot more stuff"))); Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.never()).flush(channel); Assertions.assertEquals(0, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff-more stuff", s); } @Test public void testCodingFragmentBufferingLargeFragment() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append("header"); outbuf.writeLine(chbuffer); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 100, 2); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.never()).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).flush(channel); Assertions.assertEquals(13, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("header\r\nstuff", s); } @Test public void testCodingFragmentBufferingTinyFragments() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 100, 1); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff"))); Mockito.verify(channel, Mockito.times(5)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).flush(channel); Assertions.assertEquals(18, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff---more stuff", s); } @Test public void testCodingFragmentBufferingTinyFragments2() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 100, 2); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff"))); Mockito.verify(channel, Mockito.times(4)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(2)).flush(channel); Assertions.assertEquals(18, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff---more stuff", s); } @Test public void testCodingFragmentBufferingTinyFragments3() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 100, 3); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(2, encoder.write(CodecTestUtils.wrap("--"))); Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff"))); Mockito.verify(channel, Mockito.times(4)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(5)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(2)).flush(channel); Assertions.assertEquals(21, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff------more stuff", s); } @Test public void testCodingFragmentBufferingBufferFlush() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 100, 8); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(6, encoder.write(CodecTestUtils.wrap("-stuff"))); Mockito.verify(channel, Mockito.times(1)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).flush(channel); Assertions.assertEquals(8, metrics.getBytesTransferred()); Assertions.assertEquals(3, outbuf.length()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff-stuff", s); } @Test public void testCodingFragmentBufferingBufferFlush2() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 100, 8); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(16, encoder.write(CodecTestUtils.wrap("-much more stuff"))); Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).flush(channel); Assertions.assertEquals(21, metrics.getBytesTransferred()); Assertions.assertEquals(0, outbuf.length()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff-much more stuff", s); } @Test public void testCodingFragmentBufferingChannelSaturated() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64, 8)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 100, 3); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(0, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(0, encoder.write(CodecTestUtils.wrap("more stuff"))); Mockito.verify(channel, Mockito.times(5)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(6)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(4)).flush(channel); Assertions.assertEquals(8, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff---", s); Assertions.assertEquals(3, outbuf.length()); } @Test public void testCodingFragmentBufferingChannelSaturated2() throws Exception { final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64, 8)); final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128)); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics, 100, 8); Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-"))); Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("much more stuff"))); Mockito.verify(channel, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.any()); Mockito.verify(outbuf, Mockito.times(1)).flush(channel); Assertions.assertEquals(8, metrics.getBytesTransferred()); outbuf.flush(channel); final String s = channel.dump(StandardCharsets.US_ASCII); Assertions.assertEquals("stuff--m", s); Assertions.assertEquals(0, outbuf.length()); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/CodecTestUtils.java0100664 0000000 0000000 00000004451 14245617503 026113 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; class CodecTestUtils { public static ByteBuffer wrap(final String s) { return ByteBuffer.wrap(s.getBytes(StandardCharsets.US_ASCII)); } public static String convert(final ByteBuffer src) { src.flip(); final StringBuilder buffer = new StringBuilder(src.remaining()); while (src.hasRemaining()) { buffer.append((char)(src.get() & 0xff)); } return buffer.toString(); } public static String readFromFile(final File file) throws Exception { final FileInputStream filestream = new FileInputStream(file); try (InputStreamReader reader = new InputStreamReader(filestream)) { final StringBuilder buffer = new StringBuilder(); final char[] tmp = new char[2048]; int l; while ((l = reader.read(tmp)) != -1) { buffer.append(tmp, 0, l); } return buffer.toString(); } } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/ContentEncoderMock.java0100664 0000000 0000000 00000004005 14245617503 026734 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.util.Asserts; public class ContentEncoderMock extends AbstractContentEncoder { public ContentEncoderMock( final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics) { super(channel, buffer, metrics); } @Override public int write(final ByteBuffer src) throws IOException { if (src == null) { return 0; } Asserts.check(!isCompleted(), "Decoding process already completed"); return this.channel().write(src); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestSessionInOutBuffers.java0100664 0000000 0000000 00000060214 14403631147 027767 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for {@link SessionInputBuffer} and {@link SessionOutputBuffer}. */ public class TestSessionInOutBuffers { private static WritableByteChannel newChannel(final ByteArrayOutputStream outStream) { return Channels.newChannel(outStream); } private static ReadableByteChannel newChannel(final byte[] bytes) { return Channels.newChannel(new ByteArrayInputStream(bytes)); } private static ReadableByteChannel newChannel(final String s, final Charset charset) { return Channels.newChannel(new ByteArrayInputStream(s.getBytes(charset))); } private static ReadableByteChannel newChannel(final String s) { return newChannel(s, StandardCharsets.US_ASCII); } @Test public void testReadLineChunks() throws Exception { final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16); ReadableByteChannel channel = newChannel("One\r\nTwo\r\nThree"); inbuf.fill(channel); final CharArrayBuffer line = new CharArrayBuffer(64); line.clear(); Assertions.assertTrue(inbuf.readLine(line, false)); Assertions.assertEquals("One", line.toString()); line.clear(); Assertions.assertTrue(inbuf.readLine(line, false)); Assertions.assertEquals("Two", line.toString()); line.clear(); Assertions.assertFalse(inbuf.readLine(line, false)); channel = newChannel("\r\nFour"); inbuf.fill(channel); line.clear(); Assertions.assertTrue(inbuf.readLine(line, false)); Assertions.assertEquals("Three", line.toString()); inbuf.fill(channel); line.clear(); Assertions.assertTrue(inbuf.readLine(line, true)); Assertions.assertEquals("Four", line.toString()); line.clear(); Assertions.assertFalse(inbuf.readLine(line, true)); } @Test public void testLineLimit() throws Exception { final String s = "LoooooooooooooooooooooooooOOOOOOOOOOOOOOOOOOoooooooooooooooooooooong line\r\n"; final CharArrayBuffer line = new CharArrayBuffer(64); final SessionInputBuffer inbuf1 = new SessionInputBufferImpl(128, 128); final ReadableByteChannel channel1 = newChannel(s); inbuf1.fill(channel1); Assertions.assertTrue(inbuf1.readLine(line, false)); line.clear(); final SessionInputBuffer inbuf2 = new SessionInputBufferImpl(128, 128, 10); final ReadableByteChannel channel2 = newChannel(s); inbuf2.fill(channel2); Assertions.assertThrows(MessageConstraintException.class, () -> inbuf2.readLine(line, false)); } @Test public void testLineLimitBufferFull() throws Exception { final String s = "LoooooooooooooooooooooooooOOOOOOOOOOOOOOOOOOoooooooooooooooooooooong line\r\n"; final CharArrayBuffer line = new CharArrayBuffer(64); final SessionInputBuffer inbuf1 = new SessionInputBufferImpl(32, 32); final ReadableByteChannel channel1 = newChannel(s); inbuf1.fill(channel1); Assertions.assertFalse(inbuf1.readLine(line, false)); line.clear(); final SessionInputBuffer inbuf2 = new SessionInputBufferImpl(32, 32,10); final ReadableByteChannel channel2 = newChannel(s); inbuf2.fill(channel2); Assertions.assertThrows(MessageConstraintException.class, () -> inbuf2.readLine(line, false)); } @Test public void testWriteLineChunks() throws Exception { final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(16, 16); final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0); ReadableByteChannel inChannel = newChannel("One\r\nTwo\r\nThree"); inbuf.fill(inChannel); final CharArrayBuffer line = new CharArrayBuffer(64); line.clear(); Assertions.assertTrue(inbuf.readLine(line, false)); Assertions.assertEquals("One", line.toString()); outbuf.writeLine(line); line.clear(); Assertions.assertTrue(inbuf.readLine(line, false)); Assertions.assertEquals("Two", line.toString()); outbuf.writeLine(line); line.clear(); Assertions.assertFalse(inbuf.readLine(line, false)); inChannel = newChannel("\r\nFour"); inbuf.fill(inChannel); line.clear(); Assertions.assertTrue(inbuf.readLine(line, false)); Assertions.assertEquals("Three", line.toString()); outbuf.writeLine(line); inbuf.fill(inChannel); line.clear(); Assertions.assertTrue(inbuf.readLine(line, true)); Assertions.assertEquals("Four", line.toString()); outbuf.writeLine(line); line.clear(); Assertions.assertFalse(inbuf.readLine(line, true)); final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); final WritableByteChannel outChannel = newChannel(outStream); outbuf.flush(outChannel); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("One\r\nTwo\r\nThree\r\nFour\r\n", s); } @Test public void testBasicReadWriteLine() throws Exception { final String[] teststrs = new String[5]; teststrs[0] = "Hello"; teststrs[1] = "This string should be much longer than the size of the line buffer " + "which is only 16 bytes for this test"; final StringBuilder buffer = new StringBuilder(); for (int i = 0; i < 15; i++) { buffer.append("123456789 "); } buffer.append("and stuff like that"); teststrs[2] = buffer.toString(); teststrs[3] = ""; teststrs[4] = "And goodbye"; final CharArrayBuffer chbuffer = new CharArrayBuffer(32); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16); for (final String teststr : teststrs) { chbuffer.clear(); chbuffer.append(teststr); outbuf.writeLine(chbuffer); } //this write operation should have no effect outbuf.writeLine(null); final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); final WritableByteChannel outChannel = newChannel(outStream); outbuf.flush(outChannel); final ReadableByteChannel channel = newChannel(outStream.toByteArray()); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 16, 0); inbuf.fill(channel); for (final String teststr : teststrs) { chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals(teststr, chbuffer.toString()); } chbuffer.clear(); Assertions.assertFalse(inbuf.readLine(chbuffer, true)); chbuffer.clear(); Assertions.assertFalse(inbuf.readLine(chbuffer, true)); } @Test public void testComplexReadWriteLine() throws Exception { final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16); outbuf.write(ByteBuffer.wrap(new byte[] {'a', '\n'})); outbuf.write(ByteBuffer.wrap(new byte[] {'\r', '\n'})); outbuf.write(ByteBuffer.wrap(new byte[] {'\r', '\r', '\n'})); outbuf.write(ByteBuffer.wrap(new byte[] {'\n'})); final StringBuilder buffer = new StringBuilder(); for (int i = 0; i < 14; i++) { buffer.append("a"); } final String s1 = buffer.toString(); buffer.append("\r\n"); outbuf.write(ByteBuffer.wrap(buffer.toString().getBytes(StandardCharsets.US_ASCII))); buffer.setLength(0); for (int i = 0; i < 15; i++) { buffer.append("a"); } final String s2 = buffer.toString(); buffer.append("\r\n"); outbuf.write(ByteBuffer.wrap(buffer.toString().getBytes(StandardCharsets.US_ASCII))); buffer.setLength(0); for (int i = 0; i < 16; i++) { buffer.append("a"); } final String s3 = buffer.toString(); buffer.append("\r\n"); outbuf.write(ByteBuffer.wrap(buffer.toString().getBytes(StandardCharsets.US_ASCII))); outbuf.write(ByteBuffer.wrap(new byte[] {'a'})); final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); final WritableByteChannel outChannel = newChannel(outStream); outbuf.flush(outChannel); final ReadableByteChannel channel = newChannel(outStream.toByteArray()); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 16, 0); inbuf.fill(channel); final CharArrayBuffer chbuffer = new CharArrayBuffer(32); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals("a", chbuffer.toString()); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals("", chbuffer.toString()); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals("\r", chbuffer.toString()); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals("", chbuffer.toString()); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals(s1, chbuffer.toString()); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals(s2, chbuffer.toString()); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals(s3, chbuffer.toString()); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals("a", chbuffer.toString()); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertFalse(inbuf.readLine(chbuffer, true)); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertFalse(inbuf.readLine(chbuffer, true)); } @Test public void testReadOneByte() throws Exception { // make the buffer larger than that of transmitter final byte[] out = new byte[40]; for (int i = 0; i < out.length; i++) { out[i] = (byte)('0' + i); } final ReadableByteChannel channel = newChannel(out); final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0); while (inbuf.fill(channel) > 0) { } final byte[] in = new byte[40]; for (int i = 0; i < in.length; i++) { in[i] = (byte)inbuf.read(); } for (int i = 0; i < out.length; i++) { Assertions.assertEquals(out[i], in[i]); } } @Test public void testReadByteBuffer() throws Exception { final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII); final ReadableByteChannel channel = newChannel(pattern); final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0); while (inbuf.fill(channel) > 0) { } final ByteBuffer dst = ByteBuffer.allocate(10); Assertions.assertEquals(10, inbuf.read(dst)); dst.flip(); Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 0, 10)); dst.clear(); Assertions.assertEquals(6, inbuf.read(dst)); dst.flip(); Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 10, 6)); } @Test public void testReadByteBufferWithMaxLen() throws Exception { final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII); final ReadableByteChannel channel = newChannel(pattern); final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0); while (inbuf.fill(channel) > 0) { } final ByteBuffer dst = ByteBuffer.allocate(16); Assertions.assertEquals(10, inbuf.read(dst, 10)); dst.flip(); Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 0, 10)); dst.clear(); Assertions.assertEquals(3, inbuf.read(dst, 3)); dst.flip(); Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 10, 3)); Assertions.assertEquals(3, inbuf.read(dst, 20)); dst.flip(); Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 13, 3)); } @Test public void testReadToChannel() throws Exception { final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII); final ReadableByteChannel channel = newChannel(pattern); final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0); while (inbuf.fill(channel) > 0) { } final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); final WritableByteChannel dst = newChannel(outStream); Assertions.assertEquals(16, inbuf.read(dst)); Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray())); } @Test public void testReadToChannelWithMaxLen() throws Exception { final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII); final ReadableByteChannel channel = newChannel(pattern); final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0); while (inbuf.fill(channel) > 0) { } final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); final WritableByteChannel dst = newChannel(outStream); Assertions.assertEquals(10, inbuf.read(dst, 10)); Assertions.assertEquals(3, inbuf.read(dst, 3)); Assertions.assertEquals(3, inbuf.read(dst, 10)); Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray())); } @Test public void testWriteByteBuffer() throws Exception { final byte[] pattern = "0123456789ABCDEF0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(4096, 1024); final ReadableByteChannel src = newChannel(pattern); outbuf.write(src); final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); final WritableByteChannel channel = newChannel(outStream); while (outbuf.flush(channel) > 0) { } Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray())); } @Test public void testWriteFromChannel() throws Exception { final byte[] pattern = "0123456789ABCDEF0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(4096, 1024); outbuf.write(ByteBuffer.wrap(pattern, 0, 16)); outbuf.write(ByteBuffer.wrap(pattern, 16, 10)); outbuf.write(ByteBuffer.wrap(pattern, 26, 6)); final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); final WritableByteChannel channel = newChannel(outStream); while (outbuf.flush(channel) > 0) { } Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray())); } static final int SWISS_GERMAN_HELLO [] = { 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4 }; static final int RUSSIAN_HELLO [] = { 0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438, 0x432, 0x435, 0x442 }; private static String constructString(final int [] unicodeChars) { final StringBuilder buffer = new StringBuilder(); if (unicodeChars != null) { for (final int unicodeChar : unicodeChars) { buffer.append((char)unicodeChar); } } return buffer.toString(); } @Test public void testMultibyteCodedReadWriteLine() throws Exception { final String s1 = constructString(SWISS_GERMAN_HELLO); final String s2 = constructString(RUSSIAN_HELLO); final String s3 = "Like hello and stuff"; final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16, StandardCharsets.UTF_8.newEncoder()); final CharArrayBuffer chbuffer = new CharArrayBuffer(32); for (int i = 0; i < 10; i++) { chbuffer.clear(); chbuffer.append(s1); outbuf.writeLine(chbuffer); chbuffer.clear(); chbuffer.append(s2); outbuf.writeLine(chbuffer); chbuffer.clear(); chbuffer.append(s3); outbuf.writeLine(chbuffer); } final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); final WritableByteChannel outChannel = newChannel(outStream); outbuf.flush(outChannel); final byte[] tmp = outStream.toByteArray(); final ReadableByteChannel channel = newChannel(tmp); final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0, StandardCharsets.UTF_8.newDecoder()); while (inbuf.fill(channel) > 0) { } for (int i = 0; i < 10; i++) { chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals(s1, chbuffer.toString()); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals(s2, chbuffer.toString()); chbuffer.clear(); inbuf.readLine(chbuffer, true); Assertions.assertEquals(s3, chbuffer.toString()); } } @Test public void testInputMatchesBufferLength() throws Exception { final String s1 = "abcde"; final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 5); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append(s1); outbuf.writeLine(chbuffer); } @Test public void testMalformedInputActionReport() throws Exception { final String s = constructString(SWISS_GERMAN_HELLO); final byte[] tmp = s.getBytes(StandardCharsets.ISO_8859_1); final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); decoder.onMalformedInput(CodingErrorAction.REPORT); decoder.onUnmappableCharacter(CodingErrorAction.IGNORE); final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0, decoder); final ReadableByteChannel channel = newChannel(tmp); while (inbuf.fill(channel) > 0) { } final CharArrayBuffer chbuffer = new CharArrayBuffer(16); Assertions.assertThrows(CharacterCodingException.class, () -> inbuf.readLine(chbuffer, true)); } @Test public void testMalformedInputActionIgnore() throws Exception { final String s = constructString(SWISS_GERMAN_HELLO); final byte[] tmp = s.getBytes(StandardCharsets.ISO_8859_1); final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); decoder.onMalformedInput(CodingErrorAction.IGNORE); decoder.onUnmappableCharacter(CodingErrorAction.IGNORE); final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0, decoder); final ReadableByteChannel channel = newChannel(tmp); while (inbuf.fill(channel) > 0) { } final CharArrayBuffer chbuffer = new CharArrayBuffer(16); inbuf.readLine(chbuffer, true); Assertions.assertEquals("Grezi_zm", chbuffer.toString()); } @Test public void testMalformedInputActionReplace() throws Exception { final String s = constructString(SWISS_GERMAN_HELLO); final byte[] tmp = s.getBytes(StandardCharsets.ISO_8859_1); final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); decoder.onMalformedInput(CodingErrorAction.REPLACE); decoder.onUnmappableCharacter(CodingErrorAction.IGNORE); final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0, decoder); final ReadableByteChannel channel = newChannel(tmp); while (inbuf.fill(channel) > 0) { } final CharArrayBuffer chbuffer = new CharArrayBuffer(16); inbuf.readLine(chbuffer, true); Assertions.assertEquals("Gr\ufffdezi_z\ufffdm\ufffd", chbuffer.toString()); } @Test public void testUnmappableInputActionReport() throws Exception { final String s = "This text contains a circumflex \u0302!!!"; final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder(); encoder.onMalformedInput(CodingErrorAction.IGNORE); encoder.onUnmappableCharacter(CodingErrorAction.REPORT); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16, encoder); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append(s); Assertions.assertThrows(CharacterCodingException.class, () -> outbuf.writeLine(chbuffer)); } @Test public void testUnmappableInputActionIgnore() throws Exception { final String s = "This text contains a circumflex \u0302!!!"; final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder(); encoder.onMalformedInput(CodingErrorAction.IGNORE); encoder.onUnmappableCharacter(CodingErrorAction.IGNORE); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16, encoder); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final WritableByteChannel channel = newChannel(baos); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append(s); outbuf.writeLine(chbuffer); outbuf.flush(channel); final String result = new String(baos.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("This text contains a circumflex !!!\r\n", result); } @Test public void testUnmappableInputActionReplace() throws Exception { final String s = "This text contains a circumflex \u0302 !!!"; final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder(); encoder.onMalformedInput(CodingErrorAction.IGNORE); encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16, encoder); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final WritableByteChannel channel = newChannel(baos); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append(s); outbuf.writeLine(chbuffer); outbuf.flush(channel); final String result = new String(baos.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("This text contains a circumflex ? !!!\r\n", result); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestIdentityDecoder.java0100664 0000000 0000000 00000035555 14403631147 027141 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ReadableByteChannelMock; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.nio.SessionInputBuffer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for {@link LengthDelimitedDecoder}. */ public class TestIdentityDecoder { private File tmpfile; protected File createTempFile() throws IOException { this.tmpfile = File.createTempFile("testFile", ".txt"); return this.tmpfile; } @AfterEach public void deleteTempFile() { if (this.tmpfile != null && this.tmpfile.exists()) { this.tmpfile.delete(); } } @Test public void testBasicDecoding() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff;", "more stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityDecoder decoder = new IdentityDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); int bytesRead = decoder.read(dst); Assertions.assertEquals(6, bytesRead); Assertions.assertEquals("stuff;", CodecTestUtils.convert(dst)); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(6, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(10, bytesRead); Assertions.assertEquals("more stuff", CodecTestUtils.convert(dst)); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(16, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(16, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(16, metrics.getBytesTransferred()); Assertions.assertEquals("[identity; completed: true]", decoder.toString()); } @Test public void testDecodingFromSessionBuffer() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff;", "more stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); inbuf.fill(channel); Assertions.assertEquals(6, inbuf.length()); final IdentityDecoder decoder = new IdentityDecoder(channel, inbuf, metrics); final ByteBuffer dst = ByteBuffer.allocate(1024); int bytesRead = decoder.read(dst); Assertions.assertEquals(6, bytesRead); Assertions.assertEquals("stuff;", CodecTestUtils.convert(dst)); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(0, metrics.getBytesTransferred()); // doesn't count if from session buffer dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(10, bytesRead); Assertions.assertEquals("more stuff", CodecTestUtils.convert(dst)); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(10, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(10, metrics.getBytesTransferred()); dst.clear(); bytesRead = decoder.read(dst); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(10, metrics.getBytesTransferred()); } @Test public void testBasicDecodingFile() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff; ", "more stuff; ", "a lot more stuff!"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityDecoder decoder = new IdentityDecoder( channel, inbuf, metrics); createTempFile(); try (RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) { final FileChannel fchannel = testfile.getChannel(); long pos = 0; while (!decoder.isCompleted()) { final long bytesRead = decoder.transfer(fchannel, pos, 10); if (bytesRead > 0) { pos += bytesRead; } } Assertions.assertEquals(testfile.length(), metrics.getBytesTransferred()); } Assertions.assertEquals("stuff; more stuff; a lot more stuff!", CodecTestUtils.readFromFile(this.tmpfile)); } @Test public void testDecodingFileWithBufferedSessionData() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff; ", "more stuff; ", "a lot more stuff!"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityDecoder decoder = new IdentityDecoder( channel, inbuf, metrics); final int i = inbuf.fill(channel); Assertions.assertEquals(7, i); createTempFile(); try (RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) { final FileChannel fchannel = testfile.getChannel(); long pos = 0; while (!decoder.isCompleted()) { final long bytesRead = decoder.transfer(fchannel, pos, 10); if (bytesRead > 0) { pos += bytesRead; } } // count everything except the initial 7 bytes that went to the session buffer Assertions.assertEquals(testfile.length() - 7, metrics.getBytesTransferred()); } Assertions.assertEquals("stuff; more stuff; a lot more stuff!", CodecTestUtils.readFromFile(this.tmpfile)); } @Test public void testDecodingFileWithOffsetAndBufferedSessionData() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff; ", "more stuff; ", "a lot more stuff!"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityDecoder decoder = new IdentityDecoder( channel, inbuf, metrics); final int i = inbuf.fill(channel); Assertions.assertEquals(7, i); final byte[] beginning = "beginning; ".getBytes(StandardCharsets.US_ASCII); createTempFile(); RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw"); try { testfile.write(beginning); } finally { testfile.close(); } testfile = new RandomAccessFile(this.tmpfile, "rw"); try { final FileChannel fchannel = testfile.getChannel(); long pos = beginning.length; while (!decoder.isCompleted()) { if(testfile.length() < pos) { testfile.setLength(pos); } final long bytesRead = decoder.transfer(fchannel, pos, 10); if (bytesRead > 0) { pos += bytesRead; } } // count everything except the initial 7 bytes that went to the session buffer Assertions.assertEquals(testfile.length() - 7 - beginning.length, metrics.getBytesTransferred()); } finally { testfile.close(); } Assertions.assertEquals("beginning; stuff; more stuff; a lot more stuff!", CodecTestUtils.readFromFile(this.tmpfile)); } @Test public void testDecodingFileWithLimit() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff; more stuff; ", "a lot more stuff!"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityDecoder decoder = new IdentityDecoder(channel, inbuf, metrics); final int i = inbuf.fill(channel); Assertions.assertEquals(19, i); createTempFile(); try (RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) { final FileChannel fchannel = testfile.getChannel(); long pos = 0; // transferred from buffer long bytesRead = decoder.transfer(fchannel, pos, 1); Assertions.assertEquals(1, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(0, metrics.getBytesTransferred()); pos += bytesRead; bytesRead = decoder.transfer(fchannel, pos, 2); Assertions.assertEquals(2, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(0, metrics.getBytesTransferred()); pos += bytesRead; bytesRead = decoder.transfer(fchannel, pos, 17); Assertions.assertEquals(16, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(0, metrics.getBytesTransferred()); pos += bytesRead; // transferred from channel bytesRead = decoder.transfer(fchannel, pos, 1); Assertions.assertEquals(1, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(1, metrics.getBytesTransferred()); pos += bytesRead; bytesRead = decoder.transfer(fchannel, pos, 2); Assertions.assertEquals(2, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(3, metrics.getBytesTransferred()); pos += bytesRead; bytesRead = decoder.transfer(fchannel, pos, 15); Assertions.assertEquals(14, bytesRead); Assertions.assertFalse(decoder.isCompleted()); Assertions.assertEquals(17, metrics.getBytesTransferred()); pos += bytesRead; bytesRead = decoder.transfer(fchannel, pos, 1); Assertions.assertEquals(-1, bytesRead); Assertions.assertTrue(decoder.isCompleted()); Assertions.assertEquals(17, metrics.getBytesTransferred()); } Assertions.assertEquals("stuff; more stuff; a lot more stuff!", CodecTestUtils.readFromFile(this.tmpfile)); } @Test public void testWriteBeyondFileSize() throws Exception { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"a"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityDecoder decoder = new IdentityDecoder( channel, inbuf, metrics); createTempFile(); try (RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) { Assertions.assertEquals(0, testfile.length()); final FileChannel fchannel = testfile.getChannel(); Assertions.assertThrows(IOException.class, () -> decoder.transfer(fchannel, 5, 10)); } } @Test public void testInvalidConstructor() { final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {"stuff;", "more stuff"}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); Assertions.assertThrows(NullPointerException.class, () -> new IdentityDecoder(null, null, null)); Assertions.assertThrows(NullPointerException.class, () -> new IdentityDecoder(channel, null, null)); Assertions.assertThrows(NullPointerException.class, () -> new IdentityDecoder(channel, inbuf, null)); } @Test public void testInvalidInput() throws Exception { final String s = "stuff"; final ReadableByteChannel channel = new ReadableByteChannelMock( new String[] {s}, StandardCharsets.US_ASCII); final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII); final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics(); final IdentityDecoder decoder = new IdentityDecoder(channel, inbuf, metrics); Assertions.assertThrows(NullPointerException.class, () -> decoder.read(null)); } } ././@LongLink0100644 0000000 0000000 00000000155 14403631147 011637 Lustar 0000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestAbstractHttp1StreamDuplexerCapacityWindow.javahttpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestAbstractHttp1StreamDuplexerCapacityWin0100664 0000000 0000000 00000007371 14403631147 032642 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.nio; import java.io.IOException; import java.nio.channels.SelectionKey; import org.apache.hc.core5.http.impl.nio.AbstractHttp1StreamDuplexer.CapacityWindow; import org.apache.hc.core5.reactor.IOSession; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; public class TestAbstractHttp1StreamDuplexerCapacityWindow { @Mock private IOSession ioSession; private AutoCloseable closeable; @BeforeEach public void prepareMocks() { closeable = MockitoAnnotations.openMocks(this); } @AfterEach public void releaseMocks() throws Exception { closeable.close(); } @Test public void testWindowUpdate() throws IOException { final CapacityWindow window = new CapacityWindow(0, ioSession); window.update(1); Assertions.assertEquals(1, window.getWindow()); Mockito.verify(ioSession).setEvent(Mockito.eq(SelectionKey.OP_READ)); Mockito.verifyNoMoreInteractions(ioSession); } @Test public void testRemoveCapacity() { final CapacityWindow window = new CapacityWindow(1, ioSession); window.removeCapacity(1); Assertions.assertEquals(0, window.getWindow()); Mockito.verify(ioSession).clearEvent(Mockito.eq(SelectionKey.OP_READ)); Mockito.verifyNoMoreInteractions(ioSession); } @Test public void noReadsSetAfterWindowIsClosed() throws IOException { final CapacityWindow window = new CapacityWindow(1, ioSession); window.close(); window.update(1); Mockito.verifyNoInteractions(ioSession); } @Test public void windowCannotUnderflow() { final CapacityWindow window = new CapacityWindow(Integer.MIN_VALUE, ioSession); window.removeCapacity(1); Assertions.assertEquals(Integer.MIN_VALUE, window.getWindow()); } @Test public void windowCannotOverflow() throws IOException{ final CapacityWindow window = new CapacityWindow(Integer.MAX_VALUE, ioSession); window.update(1); Assertions.assertEquals(Integer.MAX_VALUE, window.getWindow()); } @Test public void noChangesIfUpdateIsNonPositive() throws IOException { final CapacityWindow window = new CapacityWindow(1, ioSession); window.update(0); window.update(-1); Assertions.assertEquals(1, window.getWindow()); Mockito.verifyNoInteractions(ioSession); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/TestEnglishReasonPhraseCatalog.java0100664 0000000 0000000 00000006002 14403631147 030455 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import org.apache.hc.core5.http.HttpStatus; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * * Unit tests for {@link EnglishReasonPhraseCatalog} * */ public class TestEnglishReasonPhraseCatalog { @Test public void testReasonPhrases() throws IllegalAccessException { final Field[] publicFields = HttpStatus.class.getFields(); Assertions.assertNotNull( publicFields ); Assertions.assertTrue( publicFields.length > 0 ); for (final Field f : publicFields) { final int modifiers = f.getModifiers(); if ( (f.getType() == int.class) && Modifier.isPublic(modifiers) && Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers) ) { final int iValue = f.getInt(null); final String text = EnglishReasonPhraseCatalog. INSTANCE.getReason(iValue, null); Assertions.assertNotNull("text is null for HttpStatus."+f.getName(), text); Assertions.assertTrue(text.length() > 0); } } } @Test public void testStatusInvalid() throws Exception { Assertions.assertThrows(IllegalArgumentException.class, () -> EnglishReasonPhraseCatalog.INSTANCE.getReason(-1, null)); Assertions.assertThrows(IllegalArgumentException.class, () -> EnglishReasonPhraseCatalog.INSTANCE.getReason(99, null)); Assertions.assertThrows(IllegalArgumentException.class, () -> EnglishReasonPhraseCatalog.INSTANCE.getReason(600, null)); } @Test public void testStatusAll() throws Exception { for (int i = 100; i < 600; i++) { EnglishReasonPhraseCatalog.INSTANCE.getReason(i, null); } } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/0040775 0000000 0000000 00000000000 14435411677 022200 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestResponseParser.java0100664 0000000 0000000 00000014124 14403631147 026644 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayInputStream; import java.io.InterruptedIOException; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link DefaultHttpResponseParser}. */ public class TestResponseParser { @Test public void testBasicMessageParsing() throws Exception { final String s = "HTTP/1.1 200 OK\r\n" + "Server: whatever\r\n" + "Date: some date\r\n" + "Set-Cookie: c1=stuff\r\n" + "\r\n"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); final DefaultHttpResponseParser parser = new DefaultHttpResponseParser(); final ClassicHttpResponse httpresponse = parser.parse(inBuffer, inputStream); Assertions.assertEquals(200, httpresponse.getCode()); Assertions.assertEquals("OK", httpresponse.getReasonPhrase()); final Header[] headers = httpresponse.getHeaders(); Assertions.assertEquals(3, headers.length); } @Test public void testConnectionClosedException() throws Exception { final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[] {}); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final DefaultHttpResponseParser parser = new DefaultHttpResponseParser(); final ClassicHttpResponse response = parser.parse(inBuffer, inputStream); Assertions.assertNull(response); } @Test public void testBasicMessageParsingLeadingEmptyLines() throws Exception { final String s = "\r\n" + "\r\n" + "HTTP/1.1 200 OK\r\n" + "Server: whatever\r\n" + "\r\n"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); final DefaultHttpResponseParser parser = new DefaultHttpResponseParser( Http1Config.custom().setMaxEmptyLineCount(3).build()); final ClassicHttpResponse httpresponse = parser.parse(inBuffer, inputStream); Assertions.assertEquals(200, httpresponse.getCode()); Assertions.assertEquals("OK", httpresponse.getReasonPhrase()); final Header[] headers = httpresponse.getHeaders(); Assertions.assertEquals(1, headers.length); } @Test public void testBasicMessageParsingTooManyLeadingEmptyLines() throws Exception { final String s = "\r\n" + "\r\n" + "\r\n" + "HTTP/1.1 200 OK\r\n" + "Server: whatever\r\n" + "\r\n"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); final DefaultHttpResponseParser parser = new DefaultHttpResponseParser( Http1Config.custom().setMaxEmptyLineCount(3).build()); Assertions.assertThrows(MessageConstraintException.class, () -> parser.parse(inBuffer, inputStream)); } @Test public void testMessageParsingTimeout() throws Exception { final String s = "HTTP\000/1.1 200\000 OK\r\n" + "Server: wha\000tever\r\n" + "Date: some date\r\n" + "Set-Coo\000kie: c1=stuff\r\n" + "\000\r\n"; final TimeoutByteArrayInputStream inputStream = new TimeoutByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final DefaultHttpResponseParser parser = new DefaultHttpResponseParser(); int timeoutCount = 0; ClassicHttpResponse httpresponse = null; for (int i = 0; i < 10; i++) { try { httpresponse = parser.parse(inBuffer, inputStream); break; } catch (final InterruptedIOException ex) { timeoutCount++; } } Assertions.assertNotNull(httpresponse); Assertions.assertEquals(5, timeoutCount); Assertions.assertEquals(200, httpresponse.getCode()); Assertions.assertEquals("OK", httpresponse.getReasonPhrase()); final Header[] headers = httpresponse.getHeaders(); Assertions.assertEquals(3, headers.length); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestMessageParser.java0100664 0000000 0000000 00000016115 14403631147 026434 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link AbstractMessageParser}. */ public class TestMessageParser { @Test public void testBasicHeaderParsing() throws Exception { final String s = "header1: stuff\r\n" + "header2: stuff \r\n" + "header3: stuff\r\n" + " and more stuff\r\n" + "\t and even more stuff\r\n" + " \r\n" + "\r\n"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); final Header[] headers = AbstractMessageParser.parseHeaders(inBuffer, inputStream, -1, -1, null); Assertions.assertNotNull(headers); Assertions.assertEquals(3, headers.length); Assertions.assertEquals("header1", headers[0].getName()); Assertions.assertEquals("stuff", headers[0].getValue()); Assertions.assertEquals("header2", headers[1].getName()); Assertions.assertEquals("stuff", headers[1].getValue()); Assertions.assertEquals("header3", headers[2].getName()); Assertions.assertEquals("stuff and more stuff and even more stuff", headers[2].getValue()); } @Test public void testParsingHeader() throws Exception { final String s = "header1: stuff; param1 = value1; param2 = \"value 2\" \r\n" + "\r\n"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); final Header[] headers = AbstractMessageParser.parseHeaders(inBuffer, inputStream, -1, -1, null); Assertions.assertNotNull(headers); Assertions.assertEquals(1, headers.length); Assertions.assertEquals("header1: stuff; param1 = value1; param2 = \"value 2\" ", headers[0].toString()); } @Test public void testParsingInvalidHeaders() throws Exception { final String s1 = " stuff\r\n" + "header1: stuff\r\n" + "\r\n"; final ByteArrayInputStream inputStream1 = new ByteArrayInputStream(s1.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer1 = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); Assertions.assertThrows(ProtocolException.class, () -> AbstractMessageParser.parseHeaders(inBuffer1, inputStream1, -1, -1, null)); final String s2 = " : stuff\r\n" + "header1: stuff\r\n" + "\r\n"; final ByteArrayInputStream inputStream2 = new ByteArrayInputStream(s2.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer2 = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); Assertions.assertThrows(ProtocolException.class, () -> AbstractMessageParser.parseHeaders(inBuffer2, inputStream2, -1, -1, null)); } @Test public void testParsingMalformedFirstHeader() throws Exception { final String s = " header1: stuff\r\n" + "header2: stuff \r\n"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); final Header[] headers = AbstractMessageParser.parseHeaders(inBuffer, inputStream, -1, -1, null); Assertions.assertNotNull(headers); Assertions.assertEquals(2, headers.length); Assertions.assertEquals("header1", headers[0].getName()); Assertions.assertEquals("stuff", headers[0].getValue()); Assertions.assertEquals("header2", headers[1].getName()); Assertions.assertEquals("stuff", headers[1].getValue()); } @Test public void testEmptyDataStream() throws Exception { final String s = ""; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); final Header[] headers = AbstractMessageParser.parseHeaders(inBuffer, inputStream, -1, -1, null); Assertions.assertNotNull(headers); Assertions.assertEquals(0, headers.length); } @Test public void testMaxHeaderCount() throws Exception { final String s = "header1: stuff\r\n" + "header2: stuff \r\n" + "header3: stuff\r\n" + "\r\n"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); Assertions.assertThrows(IOException.class, () -> AbstractMessageParser.parseHeaders(inBuffer, inputStream, 2, -1, null)); } @Test public void testMaxHeaderCountForFoldedHeader() throws Exception { final String s = "header1: stuff\r\n" + " stuff \r\n" + " stuff\r\n" + "\r\n"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); Assertions.assertThrows(IOException.class, () -> AbstractMessageParser.parseHeaders(inBuffer, inputStream, 2, 15, null)); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TimeoutByteArrayInputStream.java0100664 0000000 0000000 00000007214 14245617503 030504 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; /** * Test class similar to {@link java.io.ByteArrayInputStream} that throws if encounters * value zero '\000' in the source byte array. */ class TimeoutByteArrayInputStream extends InputStream { private final byte[] buf; private int pos; protected final int count; public TimeoutByteArrayInputStream(final byte[] buf, final int off, final int len) { super(); this.buf = buf; this.pos = off; this.count = Math.min(off + len, buf.length); } public TimeoutByteArrayInputStream(final byte[] buf) { this(buf, 0, buf.length); } @Override public int read() throws IOException { if (this.pos < this.count) { return -1; } final int v = this.buf[this.pos++] & 0xff; if (v != 0) { return v; } throw new InterruptedIOException("Timeout"); } @Override public int read(final byte b[], final int off, final int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); } if (this.pos >= this.count) { return -1; } int chunk = len; if (this.pos + len > this.count) { chunk = this.count - this.pos; } if (chunk <= 0) { return 0; } if ((this.buf[this.pos] & 0xff) == 0) { this.pos++; throw new InterruptedIOException("Timeout"); } for (int i = 0; i < chunk; i++) { final int v = this.buf[this.pos] & 0xff; if (v == 0) { return i; } b[off + i] = (byte) v; this.pos++; } return chunk; } @Override public long skip(final long n) { long chunk = n; if (this.pos + n > this.count) { chunk = this.count - this.pos; } if (chunk < 0) { return 0; } this.pos += chunk; return chunk; } @Override public int available() { return this.count - this.pos; } @Override public boolean markSupported() { return false; } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java0100664 0000000 0000000 00000040226 14403631147 026133 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.InputStream; import java.util.List; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequestMapper; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.MethodNotSupportedException; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.UnsupportedHttpVersionException; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.HttpServerConnection; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; public class TestHttpService { @Mock private HttpProcessor httprocessor; @Mock private ConnectionReuseStrategy connReuseStrategy; @Spy private final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK); @Mock private HttpResponseFactory responseFactory; @Mock private HttpRequestMapper handlerResolver; @Mock private HttpRequestHandler requestHandler; @Mock private HttpServerConnection conn; private HttpService httpservice; @BeforeEach public void prepareMocks() { MockitoAnnotations.openMocks(this); httpservice = new HttpService( httprocessor, handlerResolver, connReuseStrategy, responseFactory); } @Test public void testInvalidInitialization() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> new HttpService( null, handlerResolver, connReuseStrategy, responseFactory)); } @Test public void testBasicExecution() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); Mockito.when(conn.receiveRequestHeader()).thenReturn(request); Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response); Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.FALSE); httpservice.handleRequest(conn, context); Assertions.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getCode()); Assertions.assertSame(request, context.getRequest()); Assertions.assertSame(response, context.getResponse()); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.verify(httprocessor).process(response, response.getEntity(), context); Mockito.verify(conn).sendResponseHeader(response); Mockito.verify(conn).sendResponseEntity(response); Mockito.verify(conn).flush(); Mockito.verify(conn).close(); Mockito.verify(response).close(); } @Test public void testExecutionEntityEnclosingRequest() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); final InputStream inStream = Mockito.mock(InputStream.class); final InputStreamEntity entity = new InputStreamEntity(inStream, -1, null); request.setEntity(entity); Mockito.when(conn.receiveRequestHeader()).thenReturn(request); Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response); Mockito.when(connReuseStrategy.keepAlive(ArgumentMatchers.eq(request), ArgumentMatchers.argThat(errorResponse -> errorResponse.getCode() == HttpStatus.SC_NOT_IMPLEMENTED), ArgumentMatchers.eq(context))).thenReturn(Boolean.TRUE); httpservice.handleRequest(conn, context); final ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class); Mockito.verify(conn).sendResponseHeader(responseCaptor.capture()); final ClassicHttpResponse response = responseCaptor.getValue(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getCode()); Assertions.assertSame(request, context.getRequest()); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.verify(inStream).close(); Mockito.verify(httprocessor).process(response, response.getEntity(), context); Mockito.verify(conn).sendResponseHeader(response); Mockito.verify(conn).sendResponseEntity(response); Mockito.verify(conn).flush(); Mockito.verify(conn, Mockito.never()).close(); Mockito.verify(response).close(); } @Test public void testExecutionEntityEnclosingRequestWithExpectContinue() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE); final InputStream inStream = Mockito.mock(InputStream.class); final InputStreamEntity entity = new InputStreamEntity(inStream, -1, null); request.setEntity(entity); Mockito.when(conn.receiveRequestHeader()).thenReturn(request); Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response); Mockito.when(connReuseStrategy.keepAlive(ArgumentMatchers.eq(request), ArgumentMatchers.argThat(errorResponse -> errorResponse.getCode() == HttpStatus.SC_NOT_IMPLEMENTED), ArgumentMatchers.eq(context))).thenReturn(Boolean.TRUE); httpservice.handleRequest(conn, context); final ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class); Mockito.verify(conn, Mockito.times(2)).sendResponseHeader(responseCaptor.capture()); final List responses = responseCaptor.getAllValues(); Assertions.assertNotNull(responses); Assertions.assertEquals(2, responses.size()); final ClassicHttpResponse ack = responses.get(0); final ClassicHttpResponse response = responses.get(1); Assertions.assertEquals(HttpStatus.SC_CONTINUE, ack.getCode()); Assertions.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getCode()); Assertions.assertSame(request, context.getRequest()); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.verify(inStream).close(); Mockito.verify(httprocessor).process(response, response.getEntity(), context); Mockito.verify(conn).sendResponseHeader(response); Mockito.verify(conn).sendResponseEntity(response); Mockito.verify(conn, Mockito.times(2)).flush(); Mockito.verify(conn, Mockito.never()).close(); Mockito.verify(response).close(); } @Test public void testMethodNotSupported() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest("whatever", "/"); Mockito.when(conn.receiveRequestHeader()).thenReturn(request); Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response); Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler); Mockito.doThrow(new MethodNotSupportedException("whatever")).when( requestHandler).handle(request, response, context); httpservice.handleRequest(conn, context); final ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class); Mockito.verify(conn).sendResponseHeader(responseCaptor.capture()); final ClassicHttpResponse error = responseCaptor.getValue(); Assertions.assertNotNull(error); Assertions.assertSame(request, context.getRequest()); Assertions.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, error.getCode()); Mockito.verify(httprocessor).process(error, error.getEntity(), context); Mockito.verify(conn).sendResponseHeader(error); Mockito.verify(conn).sendResponseEntity(error); Mockito.verify(conn).close(); } @Test public void testUnsupportedHttpVersionException() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest("whatever", "/"); Mockito.when(conn.receiveRequestHeader()).thenReturn(request); Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response); Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler); Mockito.doThrow(new UnsupportedHttpVersionException()).when( requestHandler).handle(request, response, context); httpservice.handleRequest(conn, context); final ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class); Mockito.verify(conn).sendResponseHeader(responseCaptor.capture()); final ClassicHttpResponse error = responseCaptor.getValue(); Assertions.assertNotNull(error); Assertions.assertSame(request, context.getRequest()); Assertions.assertEquals(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED, error.getCode()); Mockito.verify(httprocessor).process(error, error.getEntity(), context); Mockito.verify(conn).sendResponseHeader(error); Mockito.verify(conn).sendResponseEntity(error); Mockito.verify(conn).close(); } @Test public void testProtocolException() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest("whatever", "/"); Mockito.when(conn.receiveRequestHeader()).thenReturn(request); Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response); Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler); Mockito.doThrow(new ProtocolException("oh, this world is wrong")).when( requestHandler).handle(request, response, context); httpservice.handleRequest(conn, context); final ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class); Mockito.verify(conn).sendResponseHeader(responseCaptor.capture()); final ClassicHttpResponse error = responseCaptor.getValue(); Assertions.assertNotNull(error); Assertions.assertSame(request, context.getRequest()); Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST, error.getCode()); Mockito.verify(httprocessor).process(error, error.getEntity(), context); Mockito.verify(conn).sendResponseHeader(error); Mockito.verify(conn).sendResponseEntity(error); Mockito.verify(conn).close(); } @Test public void testConnectionKeepAlive() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); Mockito.when(conn.receiveRequestHeader()).thenReturn(request); Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response); Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler); Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.TRUE); httpservice.handleRequest(conn, context); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertSame(request, context.getRequest()); Assertions.assertSame(response, context.getResponse()); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.verify(httprocessor).process(response, response.getEntity(), context); Mockito.verify(conn).sendResponseHeader(response); Mockito.verify(conn).sendResponseEntity(response); Mockito.verify(conn).flush(); Mockito.verify(conn, Mockito.never()).close(); Mockito.verify(response).close(); } @Test public void testNoContentResponse() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); Mockito.when(conn.receiveRequestHeader()).thenReturn(request); Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response); Mockito.when(handlerResolver.resolve(request, context)).thenReturn((request1, response, context1) -> response.setCode(HttpStatus.SC_NO_CONTENT)); Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.TRUE); httpservice.handleRequest(conn, context); Assertions.assertSame(request, context.getRequest()); Mockito.verify(httprocessor).process(response, response.getEntity(), context); Mockito.verify(conn).sendResponseHeader(response); Mockito.verify(conn, Mockito.never()).sendResponseEntity(ArgumentMatchers.any()); Mockito.verify(conn).flush(); Mockito.verify(conn, Mockito.never()).close(); Mockito.verify(response).close(); } @Test public void testResponseToHead() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.HEAD, "/"); Mockito.when(conn.receiveRequestHeader()).thenReturn(request); Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response); Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler); Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.TRUE); httpservice.handleRequest(conn, context); Assertions.assertSame(request, context.getRequest()); Mockito.verify(httprocessor).process(response, response.getEntity(), context); Mockito.verify(requestHandler).handle(request, response, context); Mockito.verify(conn).sendResponseHeader(response); Mockito.verify(conn, Mockito.never()).sendResponseEntity(ArgumentMatchers.any()); Mockito.verify(conn).flush(); Mockito.verify(conn, Mockito.never()).close(); Mockito.verify(response).close(); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestDefaultBHttpClientConnection.java0100664 0000000 0000000 00000042405 14403631147 031401 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.Socket; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.LengthRequiredException; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.NotImplementedException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; public class TestDefaultBHttpClientConnection { @Mock private Socket socket; private DefaultBHttpClientConnection conn; @BeforeEach public void prepareMocks() { MockitoAnnotations.openMocks(this); conn = new DefaultBHttpClientConnection(Http1Config.DEFAULT, null, null, DefaultContentLengthStrategy.INSTANCE, DefaultContentLengthStrategy.INSTANCE, DefaultHttpRequestWriterFactory.INSTANCE, DefaultHttpResponseParserFactory.INSTANCE); } @Test public void testBasics() throws Exception { Assertions.assertFalse(conn.isOpen()); Assertions.assertEquals("[Not bound]", conn.toString()); } @Test public void testReadResponseHead() throws Exception { final String s = "HTTP/1.1 200 OK\r\nUser-Agent: test\r\n\r\n"; final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = conn.receiveResponseHeader(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertTrue(response.containsHeader("User-Agent")); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); } @Test public void testReadResponseEntityWithoutContentLength() throws Exception { final String s = "HTTP/1.1 200 OK\r\nServer: test\r\n\r\n123"; final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = conn.receiveResponseHeader(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertTrue(response.containsHeader("Server")); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); conn.receiveResponseEntity(response); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); Assertions.assertEquals(-1, entity.getContentLength()); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); final InputStream content = entity.getContent(); Assertions.assertNotNull(content); Assertions.assertEquals(3, content.available()); Assertions.assertEquals('1', content.read()); Assertions.assertEquals('2', content.read()); Assertions.assertEquals('3', content.read()); } @Test public void testReadResponseEntityWithContentLength() throws Exception { final String s = "HTTP/1.1 200 OK\r\nServer: test\r\nContent-Length: 3\r\n\r\n123"; final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = conn.receiveResponseHeader(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertTrue(response.containsHeader("Server")); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); conn.receiveResponseEntity(response); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); Assertions.assertEquals(3, entity.getContentLength()); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); final InputStream content = entity.getContent(); Assertions.assertNotNull(content); Assertions.assertTrue(content instanceof ContentLengthInputStream); } @Test public void testReadResponseEntityChunkCoded() throws Exception { final String s = "HTTP/1.1 200 OK\r\nServer: test\r\nTransfer-Encoding: " + "chunked\r\n\r\n3\r\n123\r\n0\r\n\r\n"; final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = conn.receiveResponseHeader(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertTrue(response.containsHeader("Server")); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); conn.receiveResponseEntity(response); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); Assertions.assertEquals(-1, entity.getContentLength()); Assertions.assertTrue(entity.isChunked()); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); final InputStream content = entity.getContent(); Assertions.assertNotNull(content); Assertions.assertTrue(content instanceof ChunkedInputStream); } @Test public void testReadResponseEntityIdentity() throws Exception { final String s = "HTTP/1.1 200 OK\r\nServer: test\r\nTransfer-Encoding: identity\r\n\r\n123"; final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = conn.receiveResponseHeader(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertTrue(response.containsHeader("Server")); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); Assertions.assertThrows(NotImplementedException.class, () -> conn.receiveResponseEntity(response)); } @Test public void testReadResponseNoEntity() throws Exception { final String s = "HTTP/1.1 200 OK\r\nServer: test\r\n\r\n"; final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = conn.receiveResponseHeader(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertTrue(response.containsHeader("Server")); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); conn.receiveResponseEntity(response); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); final InputStream content = entity.getContent(); Assertions.assertNotNull(content); Assertions.assertTrue(content instanceof IdentityInputStream); } @Test public void testWriteRequestHead() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/stuff"); request.addHeader("User-Agent", "test"); conn.sendRequestHeader(request); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("GET /stuff HTTP/1.1\r\nUser-Agent: test\r\n\r\n", s); } @Test public void testWriteRequestEntityWithContentLength() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff"); request.addHeader("User-Agent", "test"); request.addHeader("Content-Length", "3"); request.setEntity(new StringEntity("123", ContentType.TEXT_PLAIN)); conn.sendRequestHeader(request); conn.sendRequestEntity(request); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: 3\r\n\r\n123", s); } @Test public void testWriteRequestEntityChunkCoded() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff"); request.addHeader("User-Agent", "test"); request.addHeader("Transfer-Encoding", "chunked"); request.setEntity(new StringEntity("123", ContentType.TEXT_PLAIN)); conn.sendRequestHeader(request); conn.sendRequestEntity(request); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nTransfer-Encoding: " + "chunked\r\n\r\n3\r\n123\r\n0\r\n\r\n", s); } @Test public void testWriteRequestEntityNoContentLength() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff"); request.addHeader("User-Agent", "test"); request.setEntity(new StringEntity("123", ContentType.TEXT_PLAIN)); conn.sendRequestHeader(request); Assertions.assertThrows(LengthRequiredException.class, () -> conn.sendRequestEntity(request)); } @Test public void testWriteRequestNoEntity() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff"); request.addHeader("User-Agent", "test"); conn.sendRequestHeader(request); conn.sendRequestEntity(request); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\n\r\n", s); } @Test public void testTerminateRequestChunkedEntity() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff"); request.addHeader("User-Agent", "test"); request.addHeader("Transfer-Encoding", "chunked"); final StringEntity entity = new StringEntity("123", ContentType.TEXT_PLAIN, true); request.setEntity(entity); conn.sendRequestHeader(request); conn.terminateRequest(request); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nTransfer-Encoding: " + "chunked\r\n\r\n0\r\n\r\n", s); Assertions.assertTrue(conn.isConsistent()); } @Test public void testTerminateRequestContentLengthShort() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff"); request.addHeader("User-Agent", "test"); request.addHeader("Content-Length", "3"); final StringEntity entity = new StringEntity("123", ContentType.TEXT_PLAIN, true); request.setEntity(entity); conn.sendRequestHeader(request); conn.terminateRequest(request); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: " + "3\r\n\r\n123", s); Assertions.assertTrue(conn.isConsistent()); } @Test public void testTerminateRequestContentLengthLong() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/stuff"); request.addHeader("User-Agent", "test"); request.addHeader("Content-Length", "3000"); final ByteArrayEntity entity = new ByteArrayEntity(new byte[3000], ContentType.TEXT_PLAIN, true); request.setEntity(entity); conn.sendRequestHeader(request); conn.terminateRequest(request); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: " + "3000\r\n\r\n", s); Assertions.assertFalse(conn.isConsistent()); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestChunkCoding.java0100664 0000000 0000000 00000050741 14435411677 026103 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.MalformedChunkCodingException; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.StreamClosedException; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.apache.hc.core5.http.message.BasicHeader; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestChunkCoding { private final static String CHUNKED_INPUT = "10;key=\"value\"\r\n1234567890123456\r\n5\r\n12345\r\n0\r\nFooter1: abcde\r\nFooter2: fghij\r\n"; private final static String CHUNKED_RESULT = "123456789012345612345"; @Test public void testChunkedInputStreamLargeBuffer() throws IOException { final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(CHUNKED_INPUT.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); final byte[] buffer = new byte[300]; final ByteArrayOutputStream out = new ByteArrayOutputStream(); int len; while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } Assertions.assertEquals(-1, in.read(buffer)); Assertions.assertEquals(-1, in.read(buffer)); in.close(); final String result = new String(out.toByteArray(), StandardCharsets.ISO_8859_1); Assertions.assertEquals(result, CHUNKED_RESULT); final Header[] footers = in.getFooters(); Assertions.assertNotNull(footers); Assertions.assertEquals(2, footers.length); Assertions.assertEquals("Footer1", footers[0].getName()); Assertions.assertEquals("abcde", footers[0].getValue()); Assertions.assertEquals("Footer2", footers[1].getName()); Assertions.assertEquals("fghij", footers[1].getValue()); } //Test for when buffer is smaller than chunk size. @Test public void testChunkedInputStreamSmallBuffer() throws IOException { final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(CHUNKED_INPUT.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); final byte[] buffer = new byte[7]; final ByteArrayOutputStream out = new ByteArrayOutputStream(); int len; while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } Assertions.assertEquals(-1, in.read(buffer)); Assertions.assertEquals(-1, in.read(buffer)); in.close(); final Header[] footers = in.getFooters(); Assertions.assertNotNull(footers); Assertions.assertEquals(2, footers.length); Assertions.assertEquals("Footer1", footers[0].getName()); Assertions.assertEquals("abcde", footers[0].getValue()); Assertions.assertEquals("Footer2", footers[1].getName()); Assertions.assertEquals("fghij", footers[1].getValue()); } // One byte read @Test public void testChunkedInputStreamOneByteRead() throws IOException { final String s = "5\r\n01234\r\n5\r\n56789\r\n0\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); int ch; int i = '0'; while ((ch = in.read()) != -1) { Assertions.assertEquals(i, ch); i++; } Assertions.assertEquals(-1, in.read()); Assertions.assertEquals(-1, in.read()); in.close(); } @Test public void testAvailable() throws IOException { final String s = "5\r\n12345\r\n0\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); Assertions.assertEquals(0, in.available()); in.read(); Assertions.assertEquals(4, in.available()); in.close(); } @Test public void testChunkedInputStreamClose() throws IOException { final String s = "5\r\n01234\r\n5\r\n56789\r\n0\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); in.close(); in.close(); Assertions.assertThrows(StreamClosedException.class, () -> in.read()); final byte[] tmp = new byte[10]; Assertions.assertThrows(StreamClosedException.class, () -> in.read(tmp)); Assertions.assertThrows(StreamClosedException.class, () -> in.read(tmp, 0, tmp.length)); } // Missing closing chunk @Test public void testChunkedInputStreamNoClosingChunk() throws IOException { final String s = "5\r\n01234\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); final byte[] tmp = new byte[5]; Assertions.assertEquals(5, in.read(tmp)); Assertions.assertThrows(ConnectionClosedException.class, () -> in.read()); Assertions.assertThrows(ConnectionClosedException.class, () -> in.close()); } // Truncated stream (missing closing CRLF) @Test public void testCorruptChunkedInputStreamTruncatedCRLF() throws IOException { final String s = "5\r\n01234"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); final byte[] tmp = new byte[5]; Assertions.assertEquals(5, in.read(tmp)); Assertions.assertThrows(MalformedChunkCodingException.class, () -> in.read()); in.close(); } // Missing \r\n at the end of the first chunk @Test public void testCorruptChunkedInputStreamMissingCRLF() throws IOException { final String s = "5\r\n012345\r\n56789\r\n0\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); final byte[] buffer = new byte[300]; final ByteArrayOutputStream out = new ByteArrayOutputStream(); Assertions.assertThrows(MalformedChunkCodingException.class, () -> { int len; while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } }); in.close(); } // Missing LF @Test public void testCorruptChunkedInputStreamMissingLF() throws IOException { final String s = "5\r01234\r\n5\r\n56789\r\n0\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); Assertions.assertThrows(MalformedChunkCodingException.class, in::read); in.close(); } // Invalid chunk size @Test public void testCorruptChunkedInputStreamInvalidSize() throws IOException { final String s = "whatever\r\n01234\r\n5\r\n56789\r\n0\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); Assertions.assertThrows(MalformedChunkCodingException.class, in::read); in.close(); } // Negative chunk size @Test public void testCorruptChunkedInputStreamNegativeSize() throws IOException { final String s = "-5\r\n01234\r\n5\r\n56789\r\n0\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); Assertions.assertThrows(MalformedChunkCodingException.class, in::read); in.close(); } // Truncated chunk @Test public void testCorruptChunkedInputStreamTruncatedChunk() throws IOException { final String s = "3\r\n12"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); final byte[] buffer = new byte[300]; Assertions.assertEquals(2, in.read(buffer)); Assertions.assertThrows(MalformedChunkCodingException.class, () -> in.read(buffer)); in.close(); } // Invalid footer @Test public void testCorruptChunkedInputStreamInvalidFooter() throws IOException { final String s = "1\r\n0\r\n0\r\nstuff\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); in.read(); Assertions.assertThrows(MalformedChunkCodingException.class, in::read); in.close(); } @Test public void testCorruptChunkedInputStreamClose() throws IOException { final String s = "whatever\r\n01234\r\n5\r\n56789\r\n0\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); try (final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream)) { Assertions.assertThrows(MalformedChunkCodingException.class, in::read); } } @Test public void testEmptyChunkedInputStream() throws IOException { final String s = "0\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); final byte[] buffer = new byte[300]; final ByteArrayOutputStream out = new ByteArrayOutputStream(); int len; while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } Assertions.assertEquals(0, out.size()); in.close(); } @Test public void testTooLongChunkHeader() throws IOException { final String s = "5; and some very looooong commend\r\n12345\r\n0\r\n"; final SessionInputBuffer inBuffer1 = new SessionInputBufferImpl(16); final byte[] buffer = new byte[300]; final ByteArrayInputStream inputStream1 = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); try (final ChunkedInputStream in1 = new ChunkedInputStream(inBuffer1, inputStream1)) { Assertions.assertEquals(5, in1.read(buffer)); } final SessionInputBuffer inBuffer2 = new SessionInputBufferImpl(16, 10); final ByteArrayInputStream inputStream2 = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in2 = new ChunkedInputStream(inBuffer2, inputStream2); Assertions.assertThrows(MessageConstraintException.class, () -> in2.read(buffer)); } @Test public void testChunkedOutputStreamClose() throws IOException { final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final ChunkedOutputStream out = new ChunkedOutputStream(outbuffer, outputStream, 2048); out.close(); out.close(); Assertions.assertThrows(IOException.class, () -> out.write(new byte[] {1,2,3})); Assertions.assertThrows(IOException.class, () -> out.write(1)); } @Test public void testChunkedConsistence() throws IOException { final String input = "76126;27823abcd;:q38a-\nkjc\rk%1ad\tkh/asdui\r\njkh+?\\suweb"; final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final ChunkedOutputStream out = new ChunkedOutputStream(outbuffer, outputStream, 2048); out.write(input.getBytes(StandardCharsets.ISO_8859_1)); out.flush(); out.close(); out.close(); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); final byte[] d = new byte[10]; final ByteArrayOutputStream result = new ByteArrayOutputStream(); int len = 0; while ((len = in.read(d)) > 0) { result.write(d, 0, len); } final String output = new String(result.toByteArray(), StandardCharsets.ISO_8859_1); Assertions.assertEquals(input, output); in.close(); } @Test public void testChunkedOutputStreamWithTrailers() throws IOException { final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final ChunkedOutputStream out = new ChunkedOutputStream(outbuffer, outputStream, 2, () -> Arrays.asList( new BasicHeader("E", ""), new BasicHeader("Y", "Z")) ); out.write('x'); out.finish(); out.close(); final String content = new String(outputStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("1\r\nx\r\n0\r\nE: \r\nY: Z\r\n\r\n", content); } @Test public void testChunkedOutputStream() throws IOException { final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final ChunkedOutputStream out = new ChunkedOutputStream(outbuffer, outputStream, 2); out.write('1'); out.write('2'); out.write('3'); out.write('4'); out.finish(); out.close(); final String content = new String(outputStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("2\r\n12\r\n2\r\n34\r\n0\r\n\r\n", content); } @Test public void testChunkedOutputStreamLargeChunk() throws IOException { final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final ChunkedOutputStream out = new ChunkedOutputStream(outbuffer, outputStream, 2); out.write(new byte[] {'1', '2', '3', '4'}); out.finish(); out.close(); final String content = new String(outputStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("4\r\n1234\r\n0\r\n\r\n", content); } @Test public void testChunkedOutputStreamSmallChunk() throws IOException { final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final ChunkedOutputStream out = new ChunkedOutputStream(outbuffer, outputStream, 2); out.write('1'); out.finish(); out.close(); final String content = new String(outputStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("1\r\n1\r\n0\r\n\r\n", content); } @Test public void testResumeOnSocketTimeoutInData() throws IOException { final String s = "5\r\n01234\r\n5\r\n5\0006789\r\na\r\n0123\000456789\r\n0\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final TimeoutByteArrayInputStream inputStream = new TimeoutByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); final byte[] tmp = new byte[3]; int bytesRead = 0; int timeouts = 0; int i = 0; while (i != -1) { try { i = in.read(tmp); if (i > 0) { bytesRead += i; } } catch (final InterruptedIOException ex) { timeouts++; } } Assertions.assertEquals(20, bytesRead); Assertions.assertEquals(2, timeouts); in.close(); } @Test public void testResumeOnSocketTimeoutInChunk() throws IOException { final String s = "5\000\r\000\n\00001234\r\n\0005\r\n56789\r\na\r\n0123456789\r\n\0000\r\n"; final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final TimeoutByteArrayInputStream inputStream = new TimeoutByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); final byte[] tmp = new byte[3]; int bytesRead = 0; int timeouts = 0; int i = 0; while (i != -1) { try { i = in.read(tmp); if (i > 0) { bytesRead += i; } } catch (final InterruptedIOException ex) { timeouts++; } } Assertions.assertEquals(20, bytesRead); Assertions.assertEquals(5, timeouts); in.close(); } // Test for when buffer is larger than chunk size @Test public void testHugeChunk() throws IOException { final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream("1234567890abcdef\r\n01234567".getBytes( StandardCharsets.ISO_8859_1)); final ChunkedInputStream in = new ChunkedInputStream(inBuffer, inputStream); final ByteArrayOutputStream out = new ByteArrayOutputStream(); for (int i = 0; i < 8; ++i) { out.write(in.read()); } final String result = new String(out.toByteArray(), StandardCharsets.ISO_8859_1); Assertions.assertEquals("01234567", result); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestDefaultBHttpServerConnection.java0100664 0000000 0000000 00000034020 14403631147 031423 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.Socket; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.NotImplementedException; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; public class TestDefaultBHttpServerConnection { @Mock private Socket socket; private DefaultBHttpServerConnection conn; @BeforeEach public void prepareMocks() { MockitoAnnotations.openMocks(this); conn = new DefaultBHttpServerConnection("http", Http1Config.DEFAULT, null, null, DefaultContentLengthStrategy.INSTANCE, DefaultContentLengthStrategy.INSTANCE, DefaultHttpRequestParserFactory.INSTANCE, DefaultHttpResponseWriterFactory.INSTANCE); } @Test public void testBasics() throws Exception { Assertions.assertFalse(conn.isOpen()); Assertions.assertEquals("[Not bound]", conn.toString()); } @Test public void testReadRequestHead() throws Exception { final String s = "GET / HTTP/1.1\r\nUser-Agent: test\r\n\r\n"; final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = conn.receiveRequestHeader(); Assertions.assertNotNull(request); Assertions.assertEquals("/", request.getPath()); Assertions.assertEquals(Method.GET.name(), request.getMethod()); Assertions.assertTrue(request.containsHeader("User-Agent")); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); Assertions.assertNull(conn.getEndpointDetails().getRemoteAddress()); Assertions.assertNull(conn.getEndpointDetails().getLocalAddress()); Assertions.assertEquals(Timeout.ofMilliseconds(0), conn.getEndpointDetails().getSocketTimeout()); Assertions.assertEquals("null<->null", conn.getEndpointDetails().toString()); } @Test public void testReadRequestEntityWithContentLength() throws Exception { final String s = "POST / HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: 3\r\n\r\n123"; final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = conn.receiveRequestHeader(); Assertions.assertNotNull(request); Assertions.assertEquals("/", request.getPath()); Assertions.assertEquals(Method.POST.name(), request.getMethod()); Assertions.assertTrue(request.containsHeader("User-Agent")); Assertions.assertNull(request.getEntity()); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); conn.receiveRequestEntity(request); final HttpEntity entity = request.getEntity(); Assertions.assertNotNull(entity); Assertions.assertEquals(3, entity.getContentLength()); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); final InputStream content = entity.getContent(); Assertions.assertNotNull(content); Assertions.assertTrue(content instanceof ContentLengthInputStream); } @Test public void testReadRequestEntityChunckCoded() throws Exception { final String s = "POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nTransfer-Encoding: " + "chunked\r\n\r\n3\r\n123\r\n0\r\n\r\n"; final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = conn.receiveRequestHeader(); Assertions.assertNotNull(request); Assertions.assertEquals("/stuff", request.getPath()); Assertions.assertEquals(Method.POST.name(), request.getMethod()); Assertions.assertTrue(request.containsHeader("User-Agent")); Assertions.assertNull(request.getEntity()); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); conn.receiveRequestEntity(request); final HttpEntity entity = request.getEntity(); Assertions.assertNotNull(entity); Assertions.assertEquals(-1, entity.getContentLength()); Assertions.assertTrue(entity.isChunked()); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); final InputStream content = entity.getContent(); Assertions.assertNotNull(content); Assertions.assertTrue(content instanceof ChunkedInputStream); } @Test public void testReadRequestEntityIdentity() throws Exception { final String s = "POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nTransfer-Encoding: " + "identity\r\n\r\n123"; final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = conn.receiveRequestHeader(); Assertions.assertNotNull(request); Assertions.assertEquals("/stuff", request.getPath()); Assertions.assertEquals(Method.POST.name(), request.getMethod()); Assertions.assertTrue(request.containsHeader("User-Agent")); Assertions.assertNull(request.getEntity()); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); Assertions.assertThrows(ProtocolException.class, () -> conn.receiveRequestEntity(request)); } @Test public void testReadRequestNoEntity() throws Exception { final String s = "POST /stuff HTTP/1.1\r\nUser-Agent: test\r\n\r\n"; final ByteArrayInputStream inStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getRequestCount()); final ClassicHttpRequest request = conn.receiveRequestHeader(); Assertions.assertNotNull(request); Assertions.assertEquals("/stuff", request.getPath()); Assertions.assertEquals(Method.POST.name(), request.getMethod()); Assertions.assertTrue(request.containsHeader("User-Agent")); Assertions.assertNull(request.getEntity()); Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount()); conn.receiveRequestEntity(request); final HttpEntity entity = request.getEntity(); Assertions.assertNull(entity); } @Test public void testWriteResponseHead() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); response.addHeader("User-Agent", "test"); conn.sendResponseHeader(response); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("HTTP/1.1 200 OK\r\nUser-Agent: test\r\n\r\n", s); } @Test public void testWriteResponse100Head() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = new BasicClassicHttpResponse(100, "Go on"); conn.sendResponseHeader(response); conn.flush(); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("HTTP/1.1 100 Go on\r\n\r\n", s); } @Test public void testWriteResponseEntityWithContentLength() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); response.addHeader("Server", "test"); response.addHeader("Content-Length", "3"); response.setEntity(new StringEntity("123", ContentType.TEXT_PLAIN)); conn.sendResponseHeader(response); conn.sendResponseEntity(response); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("HTTP/1.1 200 OK\r\nServer: test\r\nContent-Length: 3\r\n\r\n123", s); } @Test public void testWriteResponseEntityChunkCoded() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); response.addHeader("Server", "test"); response.addHeader("Transfer-Encoding", "chunked"); response.setEntity(new StringEntity("123", ContentType.TEXT_PLAIN)); conn.sendResponseHeader(response); conn.sendResponseEntity(response); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("HTTP/1.1 200 OK\r\nServer: test\r\nTransfer-Encoding: " + "chunked\r\n\r\n3\r\n123\r\n0\r\n\r\n", s); } @Test public void testWriteResponseEntityIdentity() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); response.addHeader("Server", "test"); response.addHeader("Transfer-Encoding", "identity"); response.setEntity(new StringEntity("123", ContentType.TEXT_PLAIN)); conn.sendResponseHeader(response); Assertions.assertThrows(NotImplementedException.class, () -> conn.sendResponseEntity(response)); conn.flush(); } @Test public void testWriteResponseNoEntity() throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount()); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); response.addHeader("Server", "test"); conn.sendResponseHeader(response); conn.sendResponseEntity(response); conn.flush(); Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount()); final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("HTTP/1.1 200 OK\r\nServer: test\r\n\r\n", s); } } ././@LongLink0100644 0000000 0000000 00000000147 14403631147 011640 Lustar 0000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestMonitoringResponseOutOfOrderStrategy.javahttpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestMonitoringResponseOutOfOrderStrategy.ja0100664 0000000 0000000 00000012020 14403631147 032673 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.io.InputStream; import javax.net.ssl.SSLSession; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; public class TestMonitoringResponseOutOfOrderStrategy { private static final ClassicHttpRequest REQUEST = new BasicClassicHttpRequest("POST", "/path"); @Test public void testFirstByteIsNotCheckedSsl() throws IOException { final boolean earlyResponse = MonitoringResponseOutOfOrderStrategy.INSTANCE.isEarlyResponseDetected( REQUEST, connection(true, true), // SSLSocket streams report zero bytes available socketInputStream(0), 0, 1); Assertions.assertFalse(earlyResponse); } @Test public void testFirstByteIsNotCheckedPlain() throws IOException { final boolean earlyResponse = MonitoringResponseOutOfOrderStrategy.INSTANCE.isEarlyResponseDetected( REQUEST, connection(true, false), socketInputStream(1), 0, 1); Assertions.assertFalse(earlyResponse); } @Test public void testWritesWithinChunkAreNotChecked() throws IOException { final boolean earlyResponse = MonitoringResponseOutOfOrderStrategy.INSTANCE.isEarlyResponseDetected( REQUEST, connection(true, true), socketInputStream(0), 1, 8190); Assertions.assertFalse(earlyResponse, "There is data available, but checks shouldn't occur until just prior to the 8192nd byte"); } @Test public void testWritesAcrossChunksAreChecked() throws IOException { final boolean earlyResponse = MonitoringResponseOutOfOrderStrategy.INSTANCE.isEarlyResponseDetected( REQUEST, connection(true, true), socketInputStream(0), 8191, 1); Assertions.assertTrue(earlyResponse); } @Test public void testMaximumChunks() throws IOException { final ResponseOutOfOrderStrategy strategy = new MonitoringResponseOutOfOrderStrategy(1, 2); Assertions.assertTrue(strategy.isEarlyResponseDetected( REQUEST, connection(true, true), socketInputStream(0), 0, 1)); Assertions.assertTrue(strategy.isEarlyResponseDetected( REQUEST, connection(true, true), socketInputStream(0), 1, 2)); Assertions.assertFalse(strategy.isEarlyResponseDetected( REQUEST, connection(true, true), socketInputStream(0), 2, 3)); } private static InputStream socketInputStream(final int available) throws IOException { final InputStream stream = Mockito.mock(InputStream.class); Mockito.when(stream.available()).thenReturn(available); return stream; } private static HttpClientConnection connection(final boolean dataAvailable, final boolean ssl) throws IOException { final HttpClientConnection connection = Mockito.mock(HttpClientConnection.class); Mockito.when(connection.isDataAvailable(ArgumentMatchers.any(Timeout.class))).thenReturn(dataAvailable); if (ssl) { Mockito.when(connection.getSSLSession()).thenReturn(Mockito.mock(SSLSession.class)); } return connection; } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestIdentityInputStream.java0100664 0000000 0000000 00000010265 14403631147 027660 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayInputStream; import org.apache.hc.core5.http.StreamClosedException; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link IdentityInputStream}. */ public class TestIdentityInputStream { @Test public void testBasicRead() throws Exception { final byte[] input = new byte[] {'a', 'b', 'c'}; final ByteArrayInputStream inputStream = new ByteArrayInputStream(input); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final IdentityInputStream in = new IdentityInputStream(inBuffer, inputStream); final byte[] tmp = new byte[2]; Assertions.assertEquals(2, in.read(tmp, 0, tmp.length)); Assertions.assertEquals('a', tmp[0]); Assertions.assertEquals('b', tmp[1]); Assertions.assertEquals('c', in.read()); Assertions.assertEquals(-1, in.read(tmp, 0, tmp.length)); Assertions.assertEquals(-1, in.read()); Assertions.assertEquals(-1, in.read(tmp, 0, tmp.length)); Assertions.assertEquals(-1, in.read()); in.close(); } @Test public void testClosedCondition() throws Exception { final byte[] input = new byte[] {'a', 'b', 'c'}; final ByteArrayInputStream inputStream = new ByteArrayInputStream(input); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final IdentityInputStream in = new IdentityInputStream(inBuffer, inputStream); in.close(); in.close(); Assertions.assertEquals(0, in.available()); final byte[] tmp = new byte[2]; Assertions.assertThrows(StreamClosedException.class, () -> in.read(tmp, 0, tmp.length)); Assertions.assertThrows(StreamClosedException.class, () -> in.read()); } @Test public void testAvailable() throws Exception { final byte[] input = new byte[] {'a', 'b', 'c'}; final ByteArrayInputStream inputStream = new ByteArrayInputStream(input); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(new BasicHttpTransportMetrics(), 16, 16, 1024, null); final IdentityInputStream in = new IdentityInputStream(inBuffer, inputStream); in.read(); Assertions.assertEquals(2, in.available()); in.close(); } @Test public void testAvailableInStream() throws Exception { final byte[] input = new byte[] {'a', 'b', 'c', 'd', 'e', 'f'}; final ByteArrayInputStream inputStream = new ByteArrayInputStream(input); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(new BasicHttpTransportMetrics(), 16, 0, 1024, null); final IdentityInputStream in = new IdentityInputStream(inBuffer, inputStream); final byte[] tmp = new byte[3]; Assertions.assertEquals(3, in.read(tmp)); Assertions.assertEquals(3, in.available()); in.close(); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestIdentityOutputStream.java0100664 0000000 0000000 00000007755 14403631147 030073 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import org.apache.hc.core5.http.StreamClosedException; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link IdentityOutputStream}. */ public class TestIdentityOutputStream { @Test public void testBasics() throws Exception { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final OutputStream out = new IdentityOutputStream(outbuffer, outputStream); final byte[] tmp = new byte[10]; out.write(tmp, 0, 10); out.write(tmp); out.write(1); out.flush(); out.close(); final byte[] data = outputStream.toByteArray(); Assertions.assertEquals(21, data.length); } @Test public void testClose() throws Exception { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final OutputStream out = new IdentityOutputStream(outbuffer, outputStream); out.close(); out.close(); final byte[] tmp = new byte[10]; Assertions.assertThrows(IOException.class, () -> out.write(tmp)); Assertions.assertThrows(IOException.class, () -> out.write(1)); } @Test public void testBasicWrite() throws Exception { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final OutputStream out = new IdentityOutputStream(outbuffer, outputStream); out.write(new byte[] {'a', 'b'}, 0, 2); out.write('c'); out.flush(); final byte[] input = outputStream.toByteArray(); Assertions.assertNotNull(input); final byte[] expected = new byte[] {'a', 'b', 'c'}; Assertions.assertEquals(expected.length, input.length); for (int i = 0; i < expected.length; i++) { Assertions.assertEquals(expected[i], input[i]); } out.close(); } @Test public void testClosedCondition() throws Exception { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final OutputStream out = new IdentityOutputStream(outbuffer, outputStream); out.close(); out.close(); final byte[] tmp = new byte[2]; Assertions.assertThrows(StreamClosedException.class, () -> out.write(tmp, 0, tmp.length)); Assertions.assertThrows(StreamClosedException.class, () -> out.write('a')); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestBHttpConnectionBase.java0100664 0000000 0000000 00000031155 14435411677 027541 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; public class TestBHttpConnectionBase { @Mock private Socket socket; private BHttpConnectionBase conn; @BeforeEach public void prepareMocks() { MockitoAnnotations.openMocks(this); conn = new BHttpConnectionBase(Http1Config.DEFAULT, null, null); } @Test public void testBasics() throws Exception { Assertions.assertFalse(conn.isOpen()); Assertions.assertNull(conn.getLocalAddress()); Assertions.assertNull(conn.getRemoteAddress()); Assertions.assertEquals("[Not bound]", conn.toString()); } @Test public void testSocketBind() throws Exception { final InetAddress localAddress = InetAddress.getByAddress(new byte[] {127, 0, 0, 1}); final int localPort = 8888; final InetAddress remoteAddress = InetAddress.getByAddress(new byte[] {10, 0, 0, 2}); final int remotePort = 80; final InetSocketAddress localSockAddress = new InetSocketAddress(localAddress, localPort); final InetSocketAddress remoteSockAddress = new InetSocketAddress(remoteAddress, remotePort); Mockito.when(socket.getLocalSocketAddress()).thenReturn(localSockAddress); Mockito.when(socket.getRemoteSocketAddress()).thenReturn(remoteSockAddress); conn.bind(socket); Assertions.assertEquals("127.0.0.1:8888<->10.0.0.2:80", conn.toString()); Assertions.assertTrue(conn.isOpen()); Assertions.assertEquals(new InetSocketAddress( InetAddress.getByAddress(new byte[] {127, 0, 0, 1}), 8888), conn.getLocalAddress()); Assertions.assertEquals(new InetSocketAddress( InetAddress.getByAddress(new byte[] {10, 0, 0, 2}), 80), conn.getRemoteAddress()); } @Test public void testConnectionClose() throws Exception { final OutputStream outStream = Mockito.mock(OutputStream.class); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); conn.ensureOpen(); conn.outbuffer.write(0, outStream); Assertions.assertTrue(conn.isOpen()); conn.close(); Assertions.assertFalse(conn.isOpen()); Mockito.verify(outStream, Mockito.times(1)).write( ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()); Mockito.verify(socket, Mockito.times(1)).close(); conn.close(); Mockito.verify(socket, Mockito.times(1)).close(); Mockito.verify(outStream, Mockito.times(1)).write( ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()); } @Test public void testConnectionShutdown() throws Exception { final OutputStream outStream = Mockito.mock(OutputStream.class); conn.bind(socket); conn.ensureOpen(); conn.outbuffer.write(0, outStream); Assertions.assertTrue(conn.isOpen()); conn.close(CloseMode.GRACEFUL); Assertions.assertFalse(conn.isOpen()); Mockito.verify(outStream, Mockito.never()).write( ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()); Mockito.verify(socket, Mockito.never()).shutdownInput(); Mockito.verify(socket, Mockito.never()).shutdownOutput(); Mockito.verify(socket, Mockito.times(1)).close(); conn.close(); Mockito.verify(socket, Mockito.times(1)).close(); conn.close(CloseMode.GRACEFUL); Mockito.verify(socket, Mockito.times(1)).close(); } @Test public void testCreateEntityLengthDelimited() throws Exception { final InputStream inStream = Mockito.mock(InputStream.class); final ClassicHttpResponse message = new BasicClassicHttpResponse(200, "OK"); message.addHeader("Content-Length", "10"); message.addHeader("Content-Type", "stuff"); message.addHeader("Content-Encoding", "chunked"); final HttpEntity entity = conn.createIncomingEntity(message, conn.inBuffer, inStream, 10); Assertions.assertNotNull(entity); Assertions.assertFalse(entity.isChunked()); Assertions.assertEquals(10, entity.getContentLength()); Assertions.assertEquals("stuff", entity.getContentType()); Assertions.assertEquals("chunked", entity.getContentEncoding()); final InputStream content = entity.getContent(); Assertions.assertNotNull(content); Assertions.assertTrue((content instanceof ContentLengthInputStream)); } @Test public void testCreateEntityInputChunked() throws Exception { final InputStream inStream = Mockito.mock(InputStream.class); final ClassicHttpResponse message = new BasicClassicHttpResponse(200, "OK"); final HttpEntity entity = conn.createIncomingEntity(message, conn.inBuffer, inStream, ContentLengthStrategy.CHUNKED); Assertions.assertNotNull(entity); Assertions.assertTrue(entity.isChunked()); Assertions.assertEquals(-1, entity.getContentLength()); final InputStream content = entity.getContent(); Assertions.assertNotNull(content); Assertions.assertTrue((content instanceof ChunkedInputStream)); } @Test public void testCreateEntityInputUndefined() throws Exception { final InputStream inStream = Mockito.mock(InputStream.class); final ClassicHttpResponse message = new BasicClassicHttpResponse(200, "OK"); final HttpEntity entity = conn.createIncomingEntity(message, conn.inBuffer, inStream, ContentLengthStrategy.UNDEFINED); Assertions.assertNotNull(entity); Assertions.assertFalse(entity.isChunked()); Assertions.assertEquals(-1, entity.getContentLength()); final InputStream content = entity.getContent(); Assertions.assertNotNull(content); Assertions.assertTrue((content instanceof IdentityInputStream)); } @Test public void testSetSocketTimeout() throws Exception { conn.bind(socket); conn.setSocketTimeout(Timeout.ofMilliseconds(123)); Mockito.verify(socket, Mockito.times(1)).setSoTimeout(123); } @Test public void testSetSocketTimeoutException() throws Exception { conn.bind(socket); Mockito.doThrow(new SocketException()).when(socket).setSoTimeout(ArgumentMatchers.anyInt()); conn.setSocketTimeout(Timeout.ofMilliseconds(123)); Mockito.verify(socket, Mockito.times(1)).setSoTimeout(123); } @Test public void testGetSocketTimeout() throws Exception { Assertions.assertEquals(Timeout.DISABLED, conn.getSocketTimeout()); Mockito.when(socket.getSoTimeout()).thenReturn(345); conn.bind(socket); Assertions.assertEquals(Timeout.ofMilliseconds(345), conn.getSocketTimeout()); } @Test public void testGetSocketTimeoutException() throws Exception { Assertions.assertEquals(Timeout.DISABLED, conn.getSocketTimeout()); Mockito.when(socket.getSoTimeout()).thenThrow(new SocketException()); conn.bind(socket); Assertions.assertEquals(Timeout.DISABLED, conn.getSocketTimeout()); } @Test public void testAwaitInputInBuffer() throws Exception { final ByteArrayInputStream inStream = new ByteArrayInputStream( new byte[] {1, 2, 3, 4, 5}); conn.bind(socket); conn.ensureOpen(); conn.inBuffer.read(inStream); Assertions.assertTrue(conn.awaitInput(Timeout.ofMilliseconds(432))); Mockito.verify(socket, Mockito.never()).setSoTimeout(ArgumentMatchers.anyInt()); } @Test public void testAwaitInputInSocket() throws Exception { final ByteArrayInputStream inStream = new ByteArrayInputStream( new byte[] {1, 2, 3, 4, 5}); Mockito.when(socket.getInputStream()).thenReturn(inStream); Mockito.when(socket.getSoTimeout()).thenReturn(345); conn.bind(socket); conn.ensureOpen(); Assertions.assertTrue(conn.awaitInput(Timeout.ofMilliseconds(432))); Mockito.verify(socket, Mockito.times(1)).setSoTimeout(432); Mockito.verify(socket, Mockito.times(1)).setSoTimeout(345); } @Test public void testAwaitInputNoData() throws Exception { final InputStream inStream = Mockito.mock(InputStream.class); Mockito.when(socket.getInputStream()).thenReturn(inStream); Mockito.when(inStream.read(ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())) .thenReturn(-1); conn.bind(socket); conn.ensureOpen(); Assertions.assertFalse(conn.awaitInput(Timeout.ofMilliseconds(432))); } @Test public void testStaleWhenClosed() throws Exception { final OutputStream outStream = Mockito.mock(OutputStream.class); Mockito.when(socket.getOutputStream()).thenReturn(outStream); conn.bind(socket); conn.ensureOpen(); conn.close(); Assertions.assertTrue(conn.isStale()); } @Test public void testNotStaleWhenHasData() throws Exception { final ByteArrayInputStream inStream = new ByteArrayInputStream( new byte[] {1, 2, 3, 4, 5}); Mockito.when(socket.getInputStream()).thenReturn(inStream); conn.bind(socket); conn.ensureOpen(); Assertions.assertFalse(conn.isStale()); } @Test public void testStaleWhenEndOfStream() throws Exception { final InputStream inStream = Mockito.mock(InputStream.class); Mockito.when(socket.getInputStream()).thenReturn(inStream); Mockito.when(inStream.read(ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())) .thenReturn(-1); conn.bind(socket); conn.ensureOpen(); Assertions.assertTrue(conn.isStale()); } @Test public void testNotStaleWhenTimeout() throws Exception { final InputStream inStream = Mockito.mock(InputStream.class); Mockito.when(socket.getInputStream()).thenReturn(inStream); Mockito.when(inStream.read(ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())) .thenThrow(new SocketTimeoutException()); conn.bind(socket); conn.ensureOpen(); Assertions.assertFalse(conn.isStale()); } @Test public void testStaleWhenIOError() throws Exception { final InputStream inStream = Mockito.mock(InputStream.class); Mockito.when(socket.getInputStream()).thenReturn(inStream); Mockito.when(inStream.read(ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())) .thenThrow(new SocketException()); conn.bind(socket); conn.ensureOpen(); Assertions.assertTrue(conn.isStale()); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestContentLengthOutputStream.java0100664 0000000 0000000 00000005346 14403631147 031050 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import org.apache.hc.core5.http.StreamClosedException; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestContentLengthOutputStream { @Test public void testBasics() throws Exception { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final OutputStream out = new ContentLengthOutputStream(outbuffer, outputStream, 15L); final byte[] tmp = new byte[10]; out.write(tmp, 0, 10); out.write(1); out.write(tmp, 0, 10); out.write(tmp, 0, 10); out.write(tmp); out.write(1); out.write(2); out.flush(); out.close(); final byte[] data = outputStream.toByteArray(); Assertions.assertEquals(15, data.length); } @Test public void testClose() throws Exception { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final OutputStream out = new ContentLengthOutputStream(outbuffer, outputStream, 15L); out.close(); out.close(); final byte[] tmp = new byte[10]; Assertions.assertThrows(StreamClosedException.class, () -> out.write(tmp)); Assertions.assertThrows(StreamClosedException.class, () -> out.write(1)); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestSessionInOutBuffers.java0100664 0000000 0000000 00000075303 14435411677 027627 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.CharacterCodingException; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.MessageConstraintException; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.io.HttpTransportMetrics; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; public class TestSessionInOutBuffers { @Test public void testBasicBufferProperties() throws Exception { final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[] { 1, 2 , 3}); Assertions.assertEquals(16, inBuffer.capacity()); Assertions.assertEquals(16, inBuffer.available()); Assertions.assertEquals(0, inBuffer.length()); inBuffer.read(inputStream); Assertions.assertEquals(14, inBuffer.available()); Assertions.assertEquals(2, inBuffer.length()); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Assertions.assertEquals(16, outbuffer.capacity()); Assertions.assertEquals(16, outbuffer.available()); Assertions.assertEquals(0, outbuffer.length()); outbuffer.write(new byte[] {1, 2, 3}, outputStream); Assertions.assertEquals(13, outbuffer.available()); Assertions.assertEquals(3, outbuffer.length()); } @Test public void testBasicReadWriteLine() throws Exception { final String[] teststrs = new String[5]; teststrs[0] = "Hello"; teststrs[1] = "This string should be much longer than the size of the output buffer " + "which is only 16 bytes for this test"; final StringBuilder buffer = new StringBuilder(); for (int i = 0; i < 15; i++) { buffer.append("123456789 "); } buffer.append("and stuff like that"); teststrs[2] = buffer.toString(); teststrs[3] = ""; teststrs[4] = "And goodbye"; final CharArrayBuffer chbuffer = new CharArrayBuffer(16); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); for (final String teststr : teststrs) { chbuffer.clear(); chbuffer.append(teststr); outbuffer.writeLine(chbuffer, outputStream); } //these write operations should have no effect outbuffer.writeLine(null, outputStream); outbuffer.flush(outputStream); HttpTransportMetrics tmetrics = outbuffer.getMetrics(); final long bytesWritten = tmetrics.getBytesTransferred(); long expected = 0; for (final String teststr : teststrs) { expected += (teststr.length() + 2/*CRLF*/); } Assertions.assertEquals(expected, bytesWritten); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); for (final String teststr : teststrs) { chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(teststr, chbuffer.toString()); } chbuffer.clear(); Assertions.assertEquals(-1, inBuffer.readLine(chbuffer, inputStream)); chbuffer.clear(); Assertions.assertEquals(-1, inBuffer.readLine(chbuffer, inputStream)); tmetrics = inBuffer.getMetrics(); final long bytesRead = tmetrics.getBytesTransferred(); Assertions.assertEquals(expected, bytesRead); } @Test public void testComplexReadWriteLine() throws Exception { final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); outbuffer.write(new byte[] {'a', '\n'}, outputStream); outbuffer.write(new byte[] {'\r', '\n'}, outputStream); outbuffer.write(new byte[] {'\r', '\r', '\n'}, outputStream); outbuffer.write(new byte[] {'\n'},outputStream); //these write operations should have no effect outbuffer.write(null, outputStream); outbuffer.write(null, 0, 12, outputStream); outbuffer.flush(outputStream); long bytesWritten = outbuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(8, bytesWritten); final StringBuilder buffer = new StringBuilder(); for (int i = 0; i < 14; i++) { buffer.append("a"); } final String s1 = buffer.toString(); buffer.append("\r\n"); outbuffer.write(buffer.toString().getBytes(StandardCharsets.US_ASCII), outputStream); outbuffer.flush(outputStream); bytesWritten = outbuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(8 + 14 +2, bytesWritten); buffer.setLength(0); for (int i = 0; i < 15; i++) { buffer.append("a"); } final String s2 = buffer.toString(); buffer.append("\r\n"); outbuffer.write(buffer.toString().getBytes(StandardCharsets.US_ASCII), outputStream); outbuffer.flush(outputStream); bytesWritten = outbuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(8 + 14 + 2 + 15 + 2 , bytesWritten); buffer.setLength(0); for (int i = 0; i < 16; i++) { buffer.append("a"); } final String s3 = buffer.toString(); buffer.append("\r\n"); outbuffer.write(buffer.toString().getBytes(StandardCharsets.US_ASCII), outputStream); outbuffer.flush(outputStream); bytesWritten = outbuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(8 + 14 + 2 + 15 + 2 + 16 + 2, bytesWritten); outbuffer.write(new byte[] {'a'}, outputStream); outbuffer.flush(outputStream); bytesWritten = outbuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(8 + 14 + 2 + 15 + 2 + 16 + 2 + 1, bytesWritten); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals("a", chbuffer.toString()); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals("", chbuffer.toString()); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals("\r", chbuffer.toString()); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals("", chbuffer.toString()); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(s1, chbuffer.toString()); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(s2, chbuffer.toString()); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(s3, chbuffer.toString()); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals("a", chbuffer.toString()); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(-1, inBuffer.readLine(chbuffer, inputStream)); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(-1, inBuffer.readLine(chbuffer, inputStream)); final long bytesRead = inBuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(bytesWritten, bytesRead); } @Test public void testBasicReadWriteLineLargeBuffer() throws Exception { final String[] teststrs = new String[5]; teststrs[0] = "Hello"; teststrs[1] = "This string should be much longer than the size of the output buffer " + "which is only 16 bytes for this test"; final StringBuilder buffer = new StringBuilder(); for (int i = 0; i < 15; i++) { buffer.append("123456789 "); } buffer.append("and stuff like that"); teststrs[2] = buffer.toString(); teststrs[3] = ""; teststrs[4] = "And goodbye"; final CharArrayBuffer chbuffer = new CharArrayBuffer(16); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); for (final String teststr : teststrs) { chbuffer.clear(); chbuffer.append(teststr); outbuffer.writeLine(chbuffer, outputStream); } //these write operations should have no effect outbuffer.writeLine(null, outputStream); outbuffer.flush(outputStream); final long bytesWritten = outbuffer.getMetrics().getBytesTransferred(); long expected = 0; for (final String teststr : teststrs) { expected += (teststr.length() + 2/*CRLF*/); } Assertions.assertEquals(expected, bytesWritten); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(1024); final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); for (final String teststr : teststrs) { chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(teststr, chbuffer.toString()); } chbuffer.clear(); Assertions.assertEquals(-1, inBuffer.readLine(chbuffer, inputStream)); chbuffer.clear(); Assertions.assertEquals(-1, inBuffer.readLine(chbuffer, inputStream)); final long bytesRead = inBuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(expected, bytesRead); } @Test public void testReadWriteBytes() throws Exception { // make the buffer larger than that of outbuffer final byte[] out = new byte[40]; for (int i = 0; i < out.length; i++) { out[i] = (byte)('0' + i); } final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); int off = 0; int remaining = out.length; while (remaining > 0) { int chunk = 10; if (chunk > remaining) { chunk = remaining; } outbuffer.write(out, off, chunk, outputStream); off += chunk; remaining -= chunk; } outbuffer.flush(outputStream); final long bytesWritten = outbuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(out.length, bytesWritten); final byte[] tmp = outputStream.toByteArray(); Assertions.assertEquals(out.length, tmp.length); for (int i = 0; i < out.length; i++) { Assertions.assertEquals(out[i], tmp[i]); } final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(tmp); // these read operations will have no effect Assertions.assertEquals(0, inBuffer.read(null, 0, 10, inputStream)); Assertions.assertEquals(0, inBuffer.read(null, inputStream)); long bytesRead = inBuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(0, bytesRead); final byte[] in = new byte[40]; off = 0; remaining = in.length; while (remaining > 0) { int chunk = 10; if (chunk > remaining) { chunk = remaining; } final int readLen = inBuffer.read(in, off, chunk, inputStream); if (readLen == -1) { break; } off += readLen; remaining -= readLen; } for (int i = 0; i < out.length; i++) { Assertions.assertEquals(out[i], in[i]); } Assertions.assertEquals(-1, inBuffer.read(tmp, inputStream)); Assertions.assertEquals(-1, inBuffer.read(tmp, inputStream)); bytesRead = inBuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(out.length, bytesRead); } @Test public void testReadWriteByte() throws Exception { // make the buffer larger than that of outbuffer final byte[] out = new byte[40]; for (int i = 0; i < out.length; i++) { out[i] = (byte)(120 + i); } final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); for (final byte element : out) { outbuffer.write(element, outputStream); } outbuffer.flush(outputStream); final long bytesWritten = outbuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(out.length, bytesWritten); final byte[] tmp = outputStream.toByteArray(); Assertions.assertEquals(out.length, tmp.length); for (int i = 0; i < out.length; i++) { Assertions.assertEquals(out[i], tmp[i]); } final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final ByteArrayInputStream inputStream = new ByteArrayInputStream(tmp); final byte[] in = new byte[40]; for (int i = 0; i < in.length; i++) { in[i] = (byte)inBuffer.read(inputStream); } for (int i = 0; i < out.length; i++) { Assertions.assertEquals(out[i], in[i]); } Assertions.assertEquals(-1, inBuffer.read(inputStream)); Assertions.assertEquals(-1, inBuffer.read(inputStream)); final long bytesRead = inBuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(out.length, bytesRead); } @Test public void testWriteSmallFragmentBuffering() throws Exception { final OutputStream outputStream = Mockito.mock(OutputStream.class); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(new BasicHttpTransportMetrics(), 16, 16, null); outbuffer.write(1, outputStream); outbuffer.write(2, outputStream); outbuffer.write(new byte[] {1, 2}, outputStream); outbuffer.write(new byte[]{3, 4}, outputStream); outbuffer.flush(outputStream); Mockito.verify(outputStream, Mockito.times(1)).write(ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()); Mockito.verify(outputStream, Mockito.never()).write(ArgumentMatchers.anyInt()); } @Test public void testWriteSmallFragmentNoBuffering() throws Exception { final OutputStream outputStream = Mockito.mock(OutputStream.class); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(new BasicHttpTransportMetrics(), 16, 0, null); outbuffer.write(1, outputStream); outbuffer.write(2, outputStream); outbuffer.write(new byte[] {1, 2}, outputStream); outbuffer.write(new byte[]{3, 4}, outputStream); Mockito.verify(outputStream, Mockito.times(2)).write(ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()); Mockito.verify(outputStream, Mockito.times(2)).write(ArgumentMatchers.anyInt()); } @Test public void testLineLimit() throws Exception { final String s = "a very looooooooooooooooooooooooooooooooooooooooooong line\r\n"; final byte[] tmp = s.getBytes(StandardCharsets.US_ASCII); // no limit final SessionInputBuffer inBuffer1 = new SessionInputBufferImpl(5); final InputStream inputStream1 = new ByteArrayInputStream(tmp); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); inBuffer1.readLine(chbuffer, inputStream1); final long bytesRead = inBuffer1.getMetrics().getBytesTransferred(); Assertions.assertEquals(60, bytesRead); // 15 char limit final SessionInputBuffer inBuffer2 = new SessionInputBufferImpl(5, 15); final InputStream inputStream2 = new ByteArrayInputStream(tmp); chbuffer.clear(); Assertions.assertThrows(MessageConstraintException.class, () -> inBuffer2.readLine(chbuffer, inputStream2)); } @Test public void testLineLimit2() throws Exception { final String s = "just a line\r\n"; final byte[] tmp = s.getBytes(StandardCharsets.US_ASCII); // no limit final SessionInputBuffer inBuffer1 = new SessionInputBufferImpl(25); final InputStream inputStream1 = new ByteArrayInputStream(tmp); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); inBuffer1.readLine(chbuffer, inputStream1); final long bytesRead = inBuffer1.getMetrics().getBytesTransferred(); Assertions.assertEquals(13, bytesRead); // 10 char limit final SessionInputBuffer inBuffer2 = new SessionInputBufferImpl(25, 10); final InputStream inputStream2 = new ByteArrayInputStream(tmp); chbuffer.clear(); Assertions.assertThrows(MessageConstraintException.class, () -> inBuffer2.readLine(chbuffer, inputStream2)); } @Test //HTTPCORE-472 public void testLineLimit3() throws Exception { final String s = "012345678\r\nblaaaaaaaaaaaaaaaaaah"; final byte[] tmp = s.getBytes(StandardCharsets.US_ASCII); final SessionInputBuffer inBuffer1 = new SessionInputBufferImpl(128); final ByteArrayInputStream inputStream = new ByteArrayInputStream(tmp); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); inBuffer1.readLine(chbuffer, inputStream); Assertions.assertEquals("012345678", chbuffer.toString()); } @Test public void testReadLineFringeCase1() throws Exception { final String s = "abc\r\n"; final byte[] tmp = s.getBytes(StandardCharsets.US_ASCII); final SessionInputBuffer inBuffer1 = new SessionInputBufferImpl(128); final ByteArrayInputStream inputStream = new ByteArrayInputStream(tmp); Assertions.assertEquals('a', inBuffer1.read(inputStream)); Assertions.assertEquals('b', inBuffer1.read(inputStream)); Assertions.assertEquals('c', inBuffer1.read(inputStream)); Assertions.assertEquals('\r', inBuffer1.read(inputStream)); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); Assertions.assertEquals(0, inBuffer1.readLine(chbuffer, inputStream)); } static final int SWISS_GERMAN_HELLO [] = { 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4 }; static final int RUSSIAN_HELLO [] = { 0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438, 0x432, 0x435, 0x442 }; private static String constructString(final int [] unicodeChars) { final StringBuilder buffer = new StringBuilder(); if (unicodeChars != null) { for (final int unicodeChar : unicodeChars) { buffer.append((char)unicodeChar); } } return buffer.toString(); } @Test public void testMultibyteCodedReadWriteLine() throws Exception { final String s1 = constructString(SWISS_GERMAN_HELLO); final String s2 = constructString(RUSSIAN_HELLO); final String s3 = "Like hello and stuff"; final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16, StandardCharsets.UTF_8.newEncoder()); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); for (int i = 0; i < 10; i++) { chbuffer.clear(); chbuffer.append(s1); outbuffer.writeLine(chbuffer, outputStream); chbuffer.clear(); chbuffer.append(s2); outbuffer.writeLine(chbuffer, outputStream); chbuffer.clear(); chbuffer.append(s3); outbuffer.writeLine(chbuffer, outputStream); } outbuffer.flush(outputStream); final long bytesWritten = outbuffer.getMetrics().getBytesTransferred(); final long expected = ((s1.getBytes(StandardCharsets.UTF_8).length + 2)+ (s2.getBytes(StandardCharsets.UTF_8).length + 2) + (s3.getBytes(StandardCharsets.UTF_8).length + 2)) * 10; Assertions.assertEquals(expected, bytesWritten); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.UTF_8.newDecoder()); final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); for (int i = 0; i < 10; i++) { chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(s1, chbuffer.toString()); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(s2, chbuffer.toString()); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(s3, chbuffer.toString()); } chbuffer.clear(); Assertions.assertEquals(-1, inBuffer.readLine(chbuffer, inputStream)); chbuffer.clear(); Assertions.assertEquals(-1, inBuffer.readLine(chbuffer, inputStream)); final long bytesRead = inBuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(expected, bytesRead); } @Test public void testMultibyteCodedReadWriteLongLine() throws Exception { final String s1 = constructString(SWISS_GERMAN_HELLO); final String s2 = constructString(RUSSIAN_HELLO); final String s3 = "Like hello and stuff"; final StringBuilder buf = new StringBuilder(); for (int i = 0; i < 1024; i++) { buf.append(s1).append(s2).append(s3); } final String s = buf.toString(); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16, StandardCharsets.UTF_8.newEncoder()); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); chbuffer.append(s); outbuffer.writeLine(chbuffer, outputStream); outbuffer.flush(outputStream); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.UTF_8.newDecoder()); final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); chbuffer.clear(); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(s, chbuffer.toString()); } @Test public void testNonAsciiReadWriteLine() throws Exception { final String s1 = constructString(SWISS_GERMAN_HELLO); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16, StandardCharsets.ISO_8859_1.newEncoder()); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final CharArrayBuffer chbuffer = new CharArrayBuffer(16); for (int i = 0; i < 10; i++) { chbuffer.clear(); chbuffer.append(s1); outbuffer.writeLine(chbuffer, outputStream); } chbuffer.clear(); outbuffer.writeLine(chbuffer, outputStream); outbuffer.flush(outputStream); final long bytesWritten = outbuffer.getMetrics().getBytesTransferred(); final long expected = ((s1.getBytes(StandardCharsets.ISO_8859_1).length + 2)) * 10 + 2; Assertions.assertEquals(expected, bytesWritten); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.ISO_8859_1.newDecoder()); final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); for (int i = 0; i < 10; i++) { chbuffer.clear(); final int len = inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals(len, SWISS_GERMAN_HELLO.length); Assertions.assertEquals(s1, chbuffer.toString()); } chbuffer.clear(); Assertions.assertEquals(0, inBuffer.readLine(chbuffer, inputStream)); chbuffer.clear(); Assertions.assertEquals(-1, inBuffer.readLine(chbuffer, inputStream)); chbuffer.clear(); Assertions.assertEquals(-1, inBuffer.readLine(chbuffer, inputStream)); final long bytesRead = inBuffer.getMetrics().getBytesTransferred(); Assertions.assertEquals(expected, bytesRead); } @Test public void testUnmappableInputActionReport() throws Exception { final String s = "This text contains a circumflex \u0302 !!!"; final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder(); encoder.onMalformedInput(CodingErrorAction.IGNORE); encoder.onUnmappableCharacter(CodingErrorAction.REPORT); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16, encoder); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final CharArrayBuffer chbuffer = new CharArrayBuffer(32); chbuffer.append(s); Assertions.assertThrows(CharacterCodingException.class, () -> outbuffer.writeLine(chbuffer, outputStream)); } @Test public void testUnmappableInputActionReplace() throws Exception { final String s = "This text contains a circumflex \u0302 !!!"; final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder(); encoder.onMalformedInput(CodingErrorAction.IGNORE); encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16, encoder); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final CharArrayBuffer chbuffer = new CharArrayBuffer(32); chbuffer.append(s); outbuffer.writeLine(chbuffer, outputStream); outbuffer.flush(outputStream); final String result = new String(outputStream.toByteArray(), StandardCharsets.ISO_8859_1); Assertions.assertEquals("This text contains a circumflex ? !!!\r\n", result); } @Test public void testUnmappableInputActionIgnore() throws Exception { final String s = "This text contains a circumflex \u0302 !!!"; final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder(); encoder.onMalformedInput(CodingErrorAction.IGNORE); encoder.onUnmappableCharacter(CodingErrorAction.IGNORE); final SessionOutputBuffer outbuffer = new SessionOutputBufferImpl(16, encoder); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final CharArrayBuffer chbuffer = new CharArrayBuffer(32); chbuffer.append(s); outbuffer.writeLine(chbuffer, outputStream); outbuffer.flush(outputStream); final String result = new String(outputStream.toByteArray(), StandardCharsets.ISO_8859_1); Assertions.assertEquals("This text contains a circumflex !!!\r\n", result); } @Test public void testMalformedInputActionReport() throws Exception { final byte[] tmp = constructString(SWISS_GERMAN_HELLO).getBytes(StandardCharsets.ISO_8859_1); final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); decoder.onMalformedInput(CodingErrorAction.REPORT); decoder.onUnmappableCharacter(CodingErrorAction.IGNORE); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, decoder); final ByteArrayInputStream inputStream = new ByteArrayInputStream(tmp); final CharArrayBuffer chbuffer = new CharArrayBuffer(32); Assertions.assertThrows(CharacterCodingException.class, () -> inBuffer.readLine(chbuffer, inputStream)); } @Test public void testMalformedInputActionReplace() throws Exception { final byte[] tmp = constructString(SWISS_GERMAN_HELLO).getBytes(StandardCharsets.ISO_8859_1); final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); decoder.onMalformedInput(CodingErrorAction.REPLACE); decoder.onUnmappableCharacter(CodingErrorAction.IGNORE); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, decoder); final ByteArrayInputStream inputStream = new ByteArrayInputStream(tmp); final CharArrayBuffer chbuffer = new CharArrayBuffer(32); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals("Gr\ufffdezi_z\ufffdm\ufffd", chbuffer.toString()); } @Test public void testMalformedInputActionIgnore() throws Exception { final byte[] tmp = constructString(SWISS_GERMAN_HELLO).getBytes(StandardCharsets.ISO_8859_1); final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); decoder.onMalformedInput(CodingErrorAction.IGNORE); decoder.onUnmappableCharacter(CodingErrorAction.IGNORE); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, decoder); final ByteArrayInputStream inputStream = new ByteArrayInputStream(tmp); final CharArrayBuffer chbuffer = new CharArrayBuffer(32); inBuffer.readLine(chbuffer, inputStream); Assertions.assertEquals("Grezi_zm", chbuffer.toString()); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestContentLengthInputStream.java0100664 0000000 0000000 00000014240 14435411677 030651 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.StreamClosedException; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestContentLengthInputStream { @Test public void testBasics() throws IOException { final String s = "1234567890123456"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final InputStream in = new ContentLengthInputStream(inBuffer, inputStream, 10L); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final byte[] buffer = new byte[50]; int len = in.read(buffer, 0, 2); outputStream.write(buffer, 0, len); len = in.read(buffer); outputStream.write(buffer, 0, len); final String result = new String(outputStream.toByteArray(), StandardCharsets.ISO_8859_1); Assertions.assertEquals(result, "1234567890"); in.close(); } @Test public void testSkip() throws IOException { final ByteArrayInputStream inputStream1 = new ByteArrayInputStream(new byte[20]); final SessionInputBuffer inBuffer1 = new SessionInputBufferImpl(16); final InputStream in1 = new ContentLengthInputStream(inBuffer1, inputStream1, 10L); Assertions.assertEquals(10, in1.skip(10)); Assertions.assertEquals(-1, in1.read()); in1.close(); final ByteArrayInputStream inputStream2 = new ByteArrayInputStream(new byte[20]); final SessionInputBuffer inBuffer2 = new SessionInputBufferImpl(16); final InputStream in2 = new ContentLengthInputStream(inBuffer2, inputStream2, 10L); in2.read(); Assertions.assertEquals(9, in2.skip(10)); Assertions.assertEquals(-1, in2.read()); in2.close(); final ByteArrayInputStream inputStream3 = new ByteArrayInputStream(new byte[20]); final SessionInputBuffer inBuffer3 = new SessionInputBufferImpl(16); final InputStream in3 = new ContentLengthInputStream(inBuffer3, inputStream3, 2L); in3.read(); in3.read(); Assertions.assertTrue(in3.skip(10) <= 0); Assertions.assertEquals(0, in3.skip(-1)); Assertions.assertEquals(-1, in3.read()); in3.close(); final ByteArrayInputStream inputStream4 = new ByteArrayInputStream(new byte[20]); final SessionInputBuffer inBuffer4 = new SessionInputBufferImpl(16); final InputStream in4 = new ContentLengthInputStream(inBuffer4, inputStream4, 10L); Assertions.assertEquals(5,in4.skip(5)); Assertions.assertEquals(5, in4.read(new byte[20])); in4.close(); } @Test public void testAvailable() throws IOException { final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[] {1, 2, 3}); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final InputStream in = new ContentLengthInputStream(inBuffer, inputStream, 3L); Assertions.assertEquals(0, in.available()); in.read(); Assertions.assertEquals(2, in.available()); in.close(); } @Test public void testClose() throws IOException { final String s = "1234567890123456-"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final InputStream in = new ContentLengthInputStream(inBuffer, inputStream, 16L); in.close(); in.close(); Assertions.assertThrows(StreamClosedException.class, in::read); final byte[] tmp = new byte[10]; Assertions.assertThrows(StreamClosedException.class, () -> in.read(tmp)); Assertions.assertThrows(StreamClosedException.class, () -> in.read(tmp, 0, tmp.length)); Assertions.assertEquals('-', inBuffer.read(inputStream)); } @Test public void testTruncatedContent() throws IOException { final String s = "1234567890123456"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.ISO_8859_1)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final InputStream in = new ContentLengthInputStream(inBuffer, inputStream, 32L); final byte[] tmp = new byte[32]; final int byteRead = in.read(tmp); Assertions.assertEquals(16, byteRead); Assertions.assertThrows(ConnectionClosedException.class, () -> in.read(tmp)); Assertions.assertThrows(ConnectionClosedException.class, () -> in.read()); Assertions.assertThrows(ConnectionClosedException.class, () -> in.close()); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestRequestParser.java0100664 0000000 0000000 00000014176 14403631147 026505 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.ByteArrayInputStream; import java.io.InterruptedIOException; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.RequestHeaderFieldsTooLargeException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link DefaultHttpRequestParser}. */ public class TestRequestParser { @Test public void testBasicMessageParsing() throws Exception { final String s = "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "User-Agent: whatever\r\n" + "Cookie: c1=stuff\r\n" + "\r\n"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); final DefaultHttpRequestParser parser = new DefaultHttpRequestParser(); final ClassicHttpRequest httprequest = parser.parse(inBuffer, inputStream); Assertions.assertEquals(Method.GET.name(), httprequest.getMethod()); Assertions.assertEquals("/", httprequest.getPath()); final Header[] headers = httprequest.getHeaders(); Assertions.assertEquals(3, headers.length); } @Test public void testConnectionClosedException() throws Exception { final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[] {}); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final DefaultHttpRequestParser parser = new DefaultHttpRequestParser(); final ClassicHttpRequest request = parser.parse(inBuffer, inputStream); Assertions.assertNull(request); } @Test public void testBasicMessageParsingLeadingEmptyLines() throws Exception { final String s = "\r\n" + "\r\n" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); final DefaultHttpRequestParser parser = new DefaultHttpRequestParser( Http1Config.custom().setMaxEmptyLineCount(3).build()); final ClassicHttpRequest httprequest = parser.parse(inBuffer, inputStream); Assertions.assertEquals(Method.GET.name(), httprequest.getMethod()); Assertions.assertEquals("/", httprequest.getPath()); final Header[] headers = httprequest.getHeaders(); Assertions.assertEquals(1, headers.length); } @Test public void testBasicMessageParsingTooManyLeadingEmptyLines() throws Exception { final String s = "\r\n" + "\r\n" + "\r\n" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n"; final ByteArrayInputStream inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16, StandardCharsets.US_ASCII.newDecoder()); final DefaultHttpRequestParser parser = new DefaultHttpRequestParser( Http1Config.custom().setMaxEmptyLineCount(3).build()); Assertions.assertThrows(RequestHeaderFieldsTooLargeException.class, () -> parser.parse(inBuffer, inputStream)); } @Test public void testMessageParsingTimeout() throws Exception { final String s = "GET \000/ HTTP/1.1\r\000\n" + "Host: loca\000lhost\r\n" + "User-Agent: whatever\r\n" + "Coo\000kie: c1=stuff\r\n" + "\000\r\n"; final TimeoutByteArrayInputStream inputStream = new TimeoutByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII)); final SessionInputBuffer inBuffer = new SessionInputBufferImpl(16); final DefaultHttpRequestParser parser = new DefaultHttpRequestParser(); int timeoutCount = 0; ClassicHttpRequest httprequest = null; for (int i = 0; i < 10; i++) { try { httprequest = parser.parse(inBuffer, inputStream); break; } catch (final InterruptedIOException ex) { timeoutCount++; } } Assertions.assertNotNull(httprequest); Assertions.assertEquals(5, timeoutCount); Assertions.assertEquals(Method.GET.name(), httprequest.getMethod()); Assertions.assertEquals("/", httprequest.getPath()); final Header[] headers = httprequest.getHeaders(); Assertions.assertEquals(3, headers.length); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpRequestExecutor.java0100664 0000000 0000000 00000051256 14403631147 027707 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl.io; import java.io.IOException; import java.util.List; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.HttpResponseInformationCallback; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; public class TestHttpRequestExecutor { @Test public void testInvalidInput() throws Exception { final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class); Assertions.assertThrows(NullPointerException.class, () -> { final HttpRequestExecutor executor = new HttpRequestExecutor(); executor.execute(null, conn, context); }); Assertions.assertThrows(NullPointerException.class, () -> { final HttpRequestExecutor executor = new HttpRequestExecutor(); executor.execute(request, null, context); }); Assertions.assertThrows(NullPointerException.class, () -> { final HttpRequestExecutor executor = new HttpRequestExecutor(); executor.execute(request, conn, null); }); Assertions.assertThrows(NullPointerException.class, () -> { final HttpRequestExecutor executor = new HttpRequestExecutor(); executor.preProcess(null, httprocessor, context); }); Assertions.assertThrows(NullPointerException.class, () -> { final HttpRequestExecutor executor = new HttpRequestExecutor(); executor.preProcess(request, null, context); }); Assertions.assertThrows(NullPointerException.class, () -> { final HttpRequestExecutor executor = new HttpRequestExecutor(); executor.preProcess(request, httprocessor, null); }); Assertions.assertThrows(NullPointerException.class, () -> { final HttpRequestExecutor executor = new HttpRequestExecutor(); executor.postProcess(null, httprocessor, context); }); Assertions.assertThrows(NullPointerException.class, () -> { final HttpRequestExecutor executor = new HttpRequestExecutor(); executor.postProcess(response, null, context); }); Assertions.assertThrows(NullPointerException.class, () -> { final HttpRequestExecutor executor = new HttpRequestExecutor(); executor.postProcess(response, httprocessor, null); }); } @Test public void testBasicExecution() throws Exception { final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class); final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpRequestExecutor executor = new HttpRequestExecutor(); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); executor.preProcess(request, httprocessor, context); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.when(conn.receiveResponseHeader()).thenReturn( new BasicClassicHttpResponse(200, "OK")); final ClassicHttpResponse response = executor.execute(request, conn, context); Mockito.verify(conn).sendRequestHeader(request); Mockito.verify(conn).flush(); Mockito.verify(conn).receiveResponseHeader(); Mockito.verify(conn).receiveResponseEntity(response); executor.postProcess(response, httprocessor, context); Mockito.verify(httprocessor).process(response, response.getEntity(), context); Assertions.assertSame(request, context.getRequest()); Assertions.assertSame(response, context.getResponse()); } @Test public void testExecutionSkipIntermediateResponses() throws Exception { final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class); final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpRequestExecutor executor = new HttpRequestExecutor(); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); executor.preProcess(request, httprocessor, context); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.when(conn.receiveResponseHeader()).thenReturn( new BasicClassicHttpResponse(100, "Continue"), new BasicClassicHttpResponse(110, "Huh?"), new BasicClassicHttpResponse(111, "Huh?"), new BasicClassicHttpResponse(200, "OK")); final HttpResponseInformationCallback callback = Mockito.mock(HttpResponseInformationCallback.class); final ClassicHttpResponse response = executor.execute(request, conn, callback, context); Mockito.verify(conn).sendRequestHeader(request); Mockito.verify(conn).flush(); Mockito.verify(conn, Mockito.times(4)).receiveResponseHeader(); Mockito.verify(conn, Mockito.times(1)).receiveResponseEntity(response); final ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(HttpResponse.class); Mockito.verify(callback, Mockito.times(2)).execute(responseCaptor.capture(), ArgumentMatchers.eq(conn), ArgumentMatchers.eq(context)); final List infos = responseCaptor.getAllValues(); Assertions.assertNotNull(infos); Assertions.assertEquals(2, infos.size()); final HttpResponse info1 = infos.get(0); Assertions.assertNotNull(info1); Assertions.assertEquals(110, info1.getCode()); final HttpResponse info2 = infos.get(1); Assertions.assertNotNull(info2); Assertions.assertEquals(111, info2.getCode()); executor.postProcess(response, httprocessor, context); Mockito.verify(httprocessor).process(response, response.getEntity(), context); Assertions.assertSame(request, context.getRequest()); Assertions.assertSame(response, context.getResponse()); } @Test public void testExecutionNoResponseBody() throws Exception { final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class); final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpRequestExecutor executor = new HttpRequestExecutor(); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); executor.preProcess(request, httprocessor, context); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.when(conn.receiveResponseHeader()).thenReturn( new BasicClassicHttpResponse(204, "OK")); final ClassicHttpResponse response = executor.execute(request, conn, context); Mockito.verify(conn).sendRequestHeader(request); Mockito.verify(conn).flush(); Mockito.verify(conn).receiveResponseHeader(); Mockito.verify(conn, Mockito.never()).receiveResponseEntity(response); executor.postProcess(response, httprocessor, context); Mockito.verify(httprocessor).process(response, response.getEntity(), context); } @Test public void testExecutionHead() throws Exception { final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class); final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpRequestExecutor executor = new HttpRequestExecutor(); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.HEAD, "/"); executor.preProcess(request, httprocessor, context); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.when(conn.receiveResponseHeader()).thenReturn( new BasicClassicHttpResponse(200, "OK")); final ClassicHttpResponse response = executor.execute(request, conn, context); Mockito.verify(conn).sendRequestHeader(request); Mockito.verify(conn).flush(); Mockito.verify(conn).receiveResponseHeader(); Mockito.verify(conn, Mockito.never()).receiveResponseEntity(response); executor.postProcess(response, httprocessor, context); Mockito.verify(httprocessor).process(response, response.getEntity(), context); } @Test public void testExecutionEntityEnclosingRequest() throws Exception { final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class); final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpRequestExecutor executor = new HttpRequestExecutor(); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); final HttpEntity entity = Mockito.mock(HttpEntity.class); request.setEntity(entity); executor.preProcess(request, httprocessor, context); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.when(conn.receiveResponseHeader()).thenReturn( new BasicClassicHttpResponse(200, "OK")); final ClassicHttpResponse response = executor.execute(request, conn, context); Mockito.verify(conn).sendRequestHeader(request); Mockito.verify(conn).sendRequestEntity(request); Mockito.verify(conn).flush(); Mockito.verify(conn).receiveResponseHeader(); Mockito.verify(conn).receiveResponseEntity(response); executor.postProcess(response, httprocessor, context); Mockito.verify(httprocessor).process(response, response.getEntity(), context); } @Test public void testExecutionEntityEnclosingRequestWithExpectContinueSuccess() throws Exception { final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class); final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpRequestExecutor executor = new HttpRequestExecutor(); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE); final HttpEntity entity = Mockito.mock(HttpEntity.class); request.setEntity(entity); executor.preProcess(request, httprocessor, context); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.when(conn.receiveResponseHeader()).thenReturn( new BasicClassicHttpResponse(100, "Continue"), new BasicClassicHttpResponse(200, "OK")); Mockito.when(conn.isDataAvailable(ArgumentMatchers.any(Timeout.class))).thenReturn(Boolean.TRUE); final ClassicHttpResponse response = executor.execute(request, conn, context); Mockito.verify(conn).sendRequestHeader(request); Mockito.verify(conn).sendRequestEntity(request); Mockito.verify(conn, Mockito.times(2)).flush(); Mockito.verify(conn).isDataAvailable(Timeout.ofMilliseconds(3000)); Mockito.verify(conn, Mockito.times(2)).receiveResponseHeader(); Mockito.verify(conn).receiveResponseEntity(response); executor.postProcess(response, httprocessor, context); Mockito.verify(httprocessor).process(response, response.getEntity(), context); } @Test public void testExecutionEntityEnclosingRequestWithExpectContinueFailure() throws Exception { final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class); final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpRequestExecutor executor = new HttpRequestExecutor(); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE); final HttpEntity entity = Mockito.mock(HttpEntity.class); request.setEntity(entity); executor.preProcess(request, httprocessor, context); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.when(conn.receiveResponseHeader()).thenReturn( new BasicClassicHttpResponse(402, "OK")); Mockito.when(conn.isDataAvailable(ArgumentMatchers.any(Timeout.class))).thenReturn(Boolean.TRUE); final ClassicHttpResponse response = executor.execute(request, conn, context); Mockito.verify(conn).sendRequestHeader(request); Mockito.verify(conn, Mockito.never()).sendRequestEntity(request); Mockito.verify(conn).terminateRequest(request); Mockito.verify(conn, Mockito.times(2)).flush(); Mockito.verify(conn).isDataAvailable(Timeout.ofMilliseconds(3000)); Mockito.verify(conn).receiveResponseHeader(); Mockito.verify(conn).receiveResponseEntity(response); executor.postProcess(response, httprocessor, context); Mockito.verify(httprocessor).process(response, response.getEntity(), context); } @Test public void testExecutionEntityEnclosingRequestWithExpectContinueMultiple1xxResponses() throws Exception { final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class); final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpRequestExecutor executor = new HttpRequestExecutor(); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE); final HttpEntity entity = Mockito.mock(HttpEntity.class); request.setEntity(entity); executor.preProcess(request, httprocessor, context); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.when(conn.receiveResponseHeader()).thenReturn( new BasicClassicHttpResponse(110, "Huh?"), new BasicClassicHttpResponse(100, "Continue"), new BasicClassicHttpResponse(111, "Huh?"), new BasicClassicHttpResponse(200, "OK")); Mockito.when(conn.isDataAvailable(ArgumentMatchers.any(Timeout.class))).thenReturn(Boolean.TRUE); final HttpResponseInformationCallback callback = Mockito.mock(HttpResponseInformationCallback.class); final ClassicHttpResponse response = executor.execute(request, conn, callback, context); Mockito.verify(conn).sendRequestHeader(request); Mockito.verify(conn).sendRequestEntity(request); Mockito.verify(conn, Mockito.times(2)).flush(); Mockito.verify(conn, Mockito.times(2)).isDataAvailable(Timeout.ofMilliseconds(3000)); Mockito.verify(conn, Mockito.times(4)).receiveResponseHeader(); Mockito.verify(conn).receiveResponseEntity(response); final ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(HttpResponse.class); Mockito.verify(callback, Mockito.times(2)).execute(responseCaptor.capture(), ArgumentMatchers.eq(conn), ArgumentMatchers.eq(context)); final List infos = responseCaptor.getAllValues(); Assertions.assertNotNull(infos); Assertions.assertEquals(2, infos.size()); final HttpResponse info1 = infos.get(0); Assertions.assertNotNull(info1); Assertions.assertEquals(110, info1.getCode()); final HttpResponse info2 = infos.get(1); Assertions.assertNotNull(info2); Assertions.assertEquals(111, info2.getCode()); executor.postProcess(response, httprocessor, context); Mockito.verify(httprocessor).process(response, response.getEntity(), context); } @Test public void testExecutionEntityEnclosingRequestWithExpectContinueNoResponse() throws Exception { final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class); final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpRequestExecutor executor = new HttpRequestExecutor(); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/"); request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE); final HttpEntity entity = Mockito.mock(HttpEntity.class); request.setEntity(entity); executor.preProcess(request, httprocessor, context); Mockito.verify(httprocessor).process(request, request.getEntity(), context); Mockito.when(conn.receiveResponseHeader()).thenReturn( new BasicClassicHttpResponse(200, "OK")); Mockito.when(conn.isDataAvailable(ArgumentMatchers.any(Timeout.class))).thenReturn(Boolean.FALSE); final ClassicHttpResponse response = executor.execute(request, conn, context); Mockito.verify(conn).sendRequestHeader(request); Mockito.verify(conn).sendRequestEntity(request); Mockito.verify(conn, Mockito.times(2)).flush(); Mockito.verify(conn).isDataAvailable(Timeout.ofMilliseconds(3000)); Mockito.verify(conn).receiveResponseHeader(); Mockito.verify(conn).receiveResponseEntity(response); executor.postProcess(response, httprocessor, context); Mockito.verify(httprocessor).process(response, response.getEntity(), context); } @Test public void testExecutionIOException() throws Exception { final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpRequestExecutor executor = new HttpRequestExecutor(); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); Mockito.doThrow(new IOException("Oopsie")).when(conn).sendRequestHeader(request); Assertions.assertThrows(IOException.class, () -> executor.execute(request, conn, context)); Mockito.verify(conn).close(); } @Test public void testExecutionRuntimeException() throws Exception { final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); final HttpRequestExecutor executor = new HttpRequestExecutor(); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/"); Mockito.doThrow(new RuntimeException("Oopsie")).when(conn).receiveResponseHeader(); Assertions.assertThrows(RuntimeException.class, () -> executor.execute(request, conn, context)); Mockito.verify(conn).close(); } } httpcore5/src/test/java/org/apache/hc/core5/http/impl/TestDefaultConnectionReuseStrategy.java0100664 0000000 0000000 00000031330 14403631147 031413 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http.impl; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestDefaultConnectionReuseStrategy { /** HTTP context. */ private HttpContext context; /** The reuse strategy to be tested. */ private ConnectionReuseStrategy reuseStrategy; @BeforeEach public void setUp() { reuseStrategy = DefaultConnectionReuseStrategy.INSTANCE; context = new BasicHttpContext(null); } @Test public void testInvalidResponseArg() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> reuseStrategy.keepAlive(null, null, this.context)); } @Test public void testNoContentLengthResponseHttp1_0() throws Exception { context.setProtocolVersion(HttpVersion.HTTP_1_0); final HttpResponse response = new BasicHttpResponse(200, "OK"); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testNoContentLengthResponseHttp1_1() throws Exception { final HttpResponse response = new BasicHttpResponse(200, "OK"); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testChunkedContent() throws Exception { final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Transfer-Encoding", "chunked"); Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context)); } @Test public void testIgnoreInvalidKeepAlive() throws Exception { context.setProtocolVersion(HttpVersion.HTTP_1_0); final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Connection", "keep-alive"); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testExplicitClose() throws Exception { // Use HTTP 1.1 final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Transfer-Encoding", "chunked"); response.addHeader("Connection", "close"); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testExplicitKeepAlive() throws Exception { // Use HTTP 1.0 final HttpResponse response = new BasicHttpResponse(200, "OK"); response.setVersion(HttpVersion.HTTP_1_0); response.addHeader("Content-Length", "10"); response.addHeader("Connection", "keep-alive"); Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context)); } @Test public void testHTTP10Default() throws Exception { final HttpResponse response = new BasicHttpResponse(200, "OK"); response.setVersion(HttpVersion.HTTP_1_0); response.addHeader("Content-Length", "10"); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testHTTP11Default() throws Exception { final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Content-Length", "10"); Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context)); } @Test public void testBrokenConnectionDirective1() throws Exception { // Use HTTP 1.0 final HttpResponse response = new BasicHttpResponse(200, "OK"); response.setVersion(HttpVersion.HTTP_1_0); response.addHeader("Content-Length", "10"); response.addHeader("Connection", "keep--alive"); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testBrokenConnectionDirective2() throws Exception { // Use HTTP 1.0 final HttpResponse response = new BasicHttpResponse(200, "OK"); response.setVersion(HttpVersion.HTTP_1_0); response.addHeader("Content-Length", "10"); response.addHeader("Connection", null); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testConnectionTokens1() throws Exception { // Use HTTP 1.1 final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Transfer-Encoding", "chunked"); response.addHeader("Connection", "yadda, cLOSe, dumdy"); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testConnectionTokens2() throws Exception { // Use HTTP 1.1 final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Transfer-Encoding", "chunked"); response.addHeader("Connection", "yadda, kEEP-alive, dumdy"); Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context)); } @Test public void testConnectionTokens3() throws Exception { // Use HTTP 1.1 final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Transfer-Encoding", "chunked"); response.addHeader("Connection", "yadda, keep-alive, close, dumdy"); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testConnectionTokens4() throws Exception { // Use HTTP 1.1 final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Transfer-Encoding", "chunked"); response.addHeader("Connection", "yadda, close, dumdy"); response.addHeader("Proxy-Connection", "keep-alive"); // Connection takes precedence over Proxy-Connection Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testConnectionTokens5() throws Exception { // Use HTTP 1.1 final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Transfer-Encoding", "chunked"); response.addHeader("Connection", "yadda, dumdy"); response.addHeader("Proxy-Connection", "close"); // Connection takes precedence over Proxy-Connection, // even if it doesn't contain a recognized token. // Default for HTTP/1.1 is to keep alive. Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context)); } @Test public void testConnectionTokens6() throws Exception { // Use HTTP 1.1 final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Transfer-Encoding", "chunked"); response.addHeader("Connection", ""); response.addHeader("Proxy-Connection", "close"); // Connection takes precedence over Proxy-Connection, // even if it is empty. Default for HTTP/1.1 is to keep alive. Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context)); } @Test public void testMultipleContentLength() throws Exception { // Use HTTP 1.1 final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Content-Length", "10"); response.addHeader("Content-Length", "11"); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testNoContentResponse() throws Exception { // Use HTTP 1.1 final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context)); } @Test public void testNoContentResponseHttp10() throws Exception { // Use HTTP 1.0 final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); response.setVersion(HttpVersion.HTTP_1_0); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testRequestExplicitClose() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, "/"); request.addHeader("Connection", "close"); final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Transfer-Encoding", "chunked"); response.addHeader("Connection", "keep-alive"); Assertions.assertFalse(reuseStrategy.keepAlive(request, response, context)); } @Test public void testRequestNoExplicitClose() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, "/"); request.addHeader("Connection", "blah, blah, blah"); final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Transfer-Encoding", "chunked"); response.addHeader("Connection", "keep-alive"); Assertions.assertTrue(reuseStrategy.keepAlive(request, response, context)); } @Test public void testRequestExplicitCloseMultipleTokens() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, "/"); request.addHeader("Connection", "blah, blah, blah"); request.addHeader("Connection", "keep-alive"); request.addHeader("Connection", "close"); final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Transfer-Encoding", "chunked"); response.addHeader("Connection", "keep-alive"); Assertions.assertFalse(reuseStrategy.keepAlive(request, response, context)); } @Test public void testRequestClose() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.GET, "/"); request.addHeader("Connection", "close"); final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Content-Length", "10"); response.addHeader("Connection", "keep-alive"); Assertions.assertFalse(reuseStrategy.keepAlive(request, response, context)); } @Test public void testHeadRequestWithout() throws Exception { final HttpRequest request = new BasicHttpRequest(Method.HEAD, "/"); final HttpResponse response = new BasicHttpResponse(200, "OK"); Assertions.assertTrue(reuseStrategy.keepAlive(request, response, context)); } @Test public void testHttp204ContentLengthGreaterThanZero() throws Exception { final HttpResponse response = new BasicHttpResponse(204, "OK"); response.addHeader("Content-Length", "10"); response.addHeader("Connection", "keep-alive"); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } @Test public void testHttp204ContentLengthEqualToZero() throws Exception { final HttpResponse response = new BasicHttpResponse(204, "OK"); response.addHeader("Content-Length", "0"); response.addHeader("Connection", "keep-alive"); Assertions.assertTrue(reuseStrategy.keepAlive(null, response, context)); } @Test public void testHttp204ChunkedContent() throws Exception { final HttpResponse response = new BasicHttpResponse(204, "OK"); response.addHeader("Transfer-Encoding", "chunked"); response.addHeader("Connection", "keep-alive"); Assertions.assertFalse(reuseStrategy.keepAlive(null, response, context)); } } httpcore5/src/test/java/org/apache/hc/core5/http/TestHttpVersion.java0100664 0000000 0000000 00000014045 14403631147 024610 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Test cases for HTTP version class */ public class TestHttpVersion { @Test public void testEqualsMajorMinor() { Assertions.assertTrue(HttpVersion.HTTP_0_9.equals(0, 9)); Assertions.assertTrue(HttpVersion.HTTP_1_0.equals(1, 0)); Assertions.assertTrue(HttpVersion.HTTP_1_1.equals(1, 1)); Assertions.assertTrue(HttpVersion.HTTP_2.equals(2, 0)); Assertions.assertTrue(HttpVersion.HTTP_2_0.equals(2, 0)); // Assertions.assertFalse(HttpVersion.HTTP_0_9.equals(2, 0)); } @Test public void testGet() { Assertions.assertEquals(HttpVersion.HTTP_0_9, HttpVersion.get(0, 9)); Assertions.assertEquals(HttpVersion.HTTP_1_0, HttpVersion.get(1, 0)); Assertions.assertEquals(HttpVersion.HTTP_1_1, HttpVersion.get(1, 1)); Assertions.assertEquals(HttpVersion.HTTP_2_0, HttpVersion.get(2, 0)); Assertions.assertEquals(HttpVersion.HTTP_2, HttpVersion.get(2, 0)); Assertions.assertNotEquals(HttpVersion.HTTP_2_0, HttpVersion.get(2, 1)); // Assertions.assertSame(HttpVersion.HTTP_0_9, HttpVersion.get(0, 9)); Assertions.assertSame(HttpVersion.HTTP_1_0, HttpVersion.get(1, 0)); Assertions.assertSame(HttpVersion.HTTP_1_1, HttpVersion.get(1, 1)); Assertions.assertSame(HttpVersion.HTTP_2_0, HttpVersion.get(2, 0)); Assertions.assertSame(HttpVersion.HTTP_2, HttpVersion.get(2, 0)); Assertions.assertNotSame(HttpVersion.HTTP_2_0, HttpVersion.get(2, 1)); } @SuppressWarnings("unused") @Test public void testHttpVersionInvalidConstructorInput() throws Exception { Assertions.assertThrows(IllegalArgumentException.class, () -> new HttpVersion(-1, -1)); Assertions.assertThrows(IllegalArgumentException.class, () -> new HttpVersion(0, -1)); } @Test public void testHttpVersionEquality() throws Exception { final HttpVersion ver1 = new HttpVersion(1, 1); final HttpVersion ver2 = new HttpVersion(1, 1); Assertions.assertEquals(ver1.hashCode(), ver2.hashCode()); Assertions.assertEquals(ver1, ver1); Assertions.assertEquals(ver1, ver2); Assertions.assertEquals(ver1, ver1); Assertions.assertEquals(ver1, ver2); Assertions.assertFalse(ver1.equals(Float.valueOf(1.1f))); Assertions.assertEquals((new HttpVersion(0, 9)), HttpVersion.HTTP_0_9); Assertions.assertEquals((new HttpVersion(1, 0)), HttpVersion.HTTP_1_0); Assertions.assertEquals((new HttpVersion(1, 1)), HttpVersion.HTTP_1_1); Assertions.assertNotEquals((new HttpVersion(1, 1)), HttpVersion.HTTP_1_0); Assertions.assertEquals((new ProtocolVersion("HTTP", 0, 9)), HttpVersion.HTTP_0_9); Assertions.assertEquals((new ProtocolVersion("HTTP", 1, 0)), HttpVersion.HTTP_1_0); Assertions.assertEquals((new ProtocolVersion("HTTP", 1, 1)), HttpVersion.HTTP_1_1); Assertions.assertNotEquals((new ProtocolVersion("http", 1, 1)), HttpVersion.HTTP_1_1); Assertions.assertEquals(HttpVersion.HTTP_0_9, new ProtocolVersion("HTTP", 0, 9)); Assertions.assertEquals(HttpVersion.HTTP_1_0, new ProtocolVersion("HTTP", 1, 0)); Assertions.assertEquals(HttpVersion.HTTP_1_1, new ProtocolVersion("HTTP", 1, 1)); Assertions.assertNotEquals(HttpVersion.HTTP_1_1, new ProtocolVersion("http", 1, 1)); } @Test public void testHttpVersionComparison() { Assertions.assertTrue(HttpVersion.HTTP_0_9.lessEquals(HttpVersion.HTTP_1_1)); Assertions.assertTrue(HttpVersion.HTTP_0_9.greaterEquals(HttpVersion.HTTP_0_9)); Assertions.assertFalse(HttpVersion.HTTP_0_9.greaterEquals(HttpVersion.HTTP_1_0)); Assertions.assertTrue(HttpVersion.HTTP_1_0.compareToVersion(HttpVersion.HTTP_1_1) < 0); Assertions.assertTrue(HttpVersion.HTTP_1_0.compareToVersion(HttpVersion.HTTP_0_9) > 0); Assertions.assertEquals(0, HttpVersion.HTTP_1_0.compareToVersion(HttpVersion.HTTP_1_0)); } @Test public void testSerialization() throws Exception { final HttpVersion orig = HttpVersion.HTTP_1_1; final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); try (final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer)) { outStream.writeObject(orig); outStream.close(); final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final HttpVersion clone = (HttpVersion) inStream.readObject(); Assertions.assertEquals(orig, clone); } } } httpcore5/src/test/java/org/apache/hc/core5/http/TestHttpExceptions.java0100664 0000000 0000000 00000006725 14403631147 025312 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for various HTTP exception classes. */ public class TestHttpExceptions { private static final String CLEAN_MESSAGE = "[0x00]Hello[0x06][0x07][0x08][0x09][0x0a][0x0b][0x0c][0x0d][0x0e][0x0f]World"; private static final String nonPrintableMessage = String.valueOf( new char[] { 1, 'H', 'e', 'l', 'l', 'o', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 'W', 'o', 'r', 'l', 'd' }); @Test public void testConstructor() { final Throwable cause = new Exception(); new HttpException(); new HttpException("Oppsie"); new HttpException("Oppsie", cause); new ProtocolException(); new ProtocolException("Oppsie"); new ProtocolException("Oppsie", cause); new NoHttpResponseException("Oppsie"); new ConnectionClosedException("Oppsie"); new MethodNotSupportedException("Oppsie"); new MethodNotSupportedException("Oppsie", cause); new UnsupportedHttpVersionException(); new UnsupportedHttpVersionException("Oppsie"); } @Test public void testNonPrintableCharactersInConnectionClosedException() { Assertions.assertEquals(CLEAN_MESSAGE, new ConnectionClosedException(nonPrintableMessage).getMessage()); } @Test public void testNonPrintableCharactersInHttpException() { Assertions.assertEquals(CLEAN_MESSAGE, new HttpException(nonPrintableMessage).getMessage()); } @Test public void testNonPrintableCharactersInMethodNotSupportedException() { Assertions.assertEquals(CLEAN_MESSAGE, new MethodNotSupportedException(nonPrintableMessage).getMessage()); } @Test public void testNonPrintableCharactersInNoHttpResponseException() { Assertions.assertEquals(CLEAN_MESSAGE, new NoHttpResponseException(nonPrintableMessage).getMessage()); } @Test public void testNonPrintableCharactersInProtocolException() { Assertions.assertEquals(CLEAN_MESSAGE, new ProtocolException(nonPrintableMessage).getMessage()); } @Test public void testNonPrintableCharactersInUnsupportedHttpVersionException() { Assertions.assertEquals(CLEAN_MESSAGE, new UnsupportedHttpVersionException(nonPrintableMessage).getMessage()); } } httpcore5/src/test/java/org/apache/hc/core5/http/WritableByteChannelMock.java0100664 0000000 0000000 00000007506 14245617503 026173 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.WritableByteChannel; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; public class WritableByteChannelMock implements WritableByteChannel { private final int capacityLimit; private int capacityUsed; private ByteBuffer buf; private boolean closed; public WritableByteChannelMock(final int initialSize, final int capacityLimit) { this.buf = ByteBuffer.allocate(initialSize); this.capacityLimit = capacityLimit; } public WritableByteChannelMock(final int initialSize) { this(initialSize, 0); } private void expandCapacity(final int capacity) { final ByteBuffer oldbuffer = this.buf; this.buf = ByteBuffer.allocate(capacity); oldbuffer.flip(); this.buf.put(oldbuffer); } private void ensureCapacity(final int requiredCapacity) { if (requiredCapacity > this.buf.capacity()) { expandCapacity(requiredCapacity); } } @Override public int write(final ByteBuffer src) throws IOException { if (this.closed) { throw new ClosedChannelException(); } final int len = src.remaining(); ensureCapacity(this.buf.position() + len); if (this.capacityLimit > 0) { final int chunk = Math.min(this.capacityLimit - this.capacityUsed, len); if (chunk > 0) { final int limit = src.limit(); src.limit(src.position() + chunk); this.buf.put(src); src.limit(limit); this.capacityUsed += chunk; return chunk; } return 0; } this.buf.put(src); return len; } @Override public boolean isOpen() { return !this.closed; } @Override public void close() throws IOException { this.closed = true; } public void flush() { this.capacityUsed = 0; } public void reset() { this.capacityUsed = 0; this.buf.clear(); } public byte[] toByteArray() { final ByteBuffer dup = this.buf.duplicate(); dup.flip(); final byte[] bytes = new byte[dup.remaining()]; dup.get(bytes); return bytes; } public String dump(final Charset charset) throws CharacterCodingException { this.buf.flip(); final CharBuffer charBuffer = charset.newDecoder().decode(this.buf); this.buf.compact(); return charBuffer.toString(); } } httpcore5/src/test/java/org/apache/hc/core5/concurrent/0040775 0000000 0000000 00000000000 14403631147 022022 5ustar000000000 0000000 httpcore5/src/test/java/org/apache/hc/core5/concurrent/TestComplexFuture.java0100664 0000000 0000000 00000007373 14403631147 026336 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; import static org.hamcrest.MatcherAssert.assertThat; import java.util.concurrent.Future; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestComplexFuture { @Test public void testCancelled() throws Exception { final ComplexFuture future = new ComplexFuture<>(null); final Future dependency1 = new BasicFuture<>(null); future.setDependency(dependency1); Assertions.assertFalse(future.isDone()); future.cancel(); assertThat(future.isCancelled(), CoreMatchers.is(true)); assertThat(dependency1.isCancelled(), CoreMatchers.is(true)); final Future dependency2 = new BasicFuture<>(null); future.setDependency(dependency2); assertThat(dependency2.isCancelled(), CoreMatchers.is(true)); } @Test public void testCompleted() throws Exception { final ComplexFuture future = new ComplexFuture<>(null); final Future dependency1 = new BasicFuture<>(null); future.setDependency(dependency1); Assertions.assertFalse(future.isDone()); future.completed(Boolean.TRUE); assertThat(future.isCancelled(), CoreMatchers.is(false)); assertThat(dependency1.isCancelled(), CoreMatchers.is(false)); final Future dependency2 = new BasicFuture<>(null); future.setDependency(dependency2); assertThat(dependency2.isCancelled(), CoreMatchers.is(true)); } @Test public void testCancelledWithCallback() throws Exception { final ComplexFuture future = new ComplexFuture<>(null); final Future dependency1 = new BasicFuture<>(new FutureContribution(future) { @Override public void completed(final Object result) { } }); future.setDependency(dependency1); Assertions.assertFalse(future.isDone()); future.cancel(); assertThat(future.isCancelled(), CoreMatchers.is(true)); assertThat(dependency1.isCancelled(), CoreMatchers.is(true)); final Future dependency2 = new BasicFuture<>(null); future.setDependency(dependency2); assertThat(dependency2.isCancelled(), CoreMatchers.is(true)); } @Test public void testCanceledAndFailed() { final ComplexFuture future = new ComplexFuture<>(null); assertThat(future.cancel(), CoreMatchers.is(true)); assertThat(future.failed(new Exception()), CoreMatchers.is(false)); } } httpcore5/src/test/java/org/apache/hc/core5/concurrent/TestComplexCancellable.java0100664 0000000 0000000 00000004105 14403631147 027237 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; import static org.hamcrest.MatcherAssert.assertThat; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestComplexCancellable { @Test public void testCancelled() throws Exception { final ComplexCancellable cancellable = new ComplexCancellable(); final BasicFuture dependency1 = new BasicFuture<>(null); cancellable.setDependency(dependency1); Assertions.assertFalse(cancellable.isCancelled()); cancellable.cancel(); assertThat(cancellable.isCancelled(), CoreMatchers.is(true)); assertThat(dependency1.isCancelled(), CoreMatchers.is(true)); final BasicFuture dependency2 = new BasicFuture<>(null); cancellable.setDependency(dependency2); assertThat(dependency2.isCancelled(), CoreMatchers.is(true)); } } httpcore5/src/test/java/org/apache/hc/core5/concurrent/TestBasicFuture.java0100664 0000000 0000000 00000016505 14403631147 025745 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.hc.core5.util.TimeoutValueException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestBasicFuture { @Test public void testCompleted() throws Exception { final FutureCallback callback = Mockito.mock(FutureCallback.class); final BasicFuture future = new BasicFuture<>(callback); Assertions.assertFalse(future.isDone()); final Object result = new Object(); final Exception boom = new Exception(); future.completed(result); future.failed(boom); Mockito.verify(callback).completed(result); Mockito.verify(callback, Mockito.never()).failed(Mockito.any()); Mockito.verify(callback, Mockito.never()).cancelled(); Assertions.assertSame(result, future.get()); Assertions.assertTrue(future.isDone()); Assertions.assertFalse(future.isCancelled()); } @Test public void testCompletedWithTimeout() throws Exception { final FutureCallback callback = Mockito.mock(FutureCallback.class); final BasicFuture future = new BasicFuture<>(callback); Assertions.assertFalse(future.isDone()); final Object result = new Object(); final Exception boom = new Exception(); future.completed(result); future.failed(boom); Mockito.verify(callback).completed(result); Mockito.verify(callback, Mockito.never()).failed(Mockito.any()); Mockito.verify(callback, Mockito.never()).cancelled(); Assertions.assertSame(result, future.get(1, TimeUnit.MILLISECONDS)); Assertions.assertTrue(future.isDone()); Assertions.assertFalse(future.isCancelled()); } @Test public void testFailed() throws Exception { final FutureCallback callback = Mockito.mock(FutureCallback.class); final BasicFuture future = new BasicFuture<>(callback); final Object result = new Object(); final Exception boom = new Exception(); future.failed(boom); future.completed(result); Mockito.verify(callback, Mockito.never()).completed(Mockito.any()); Mockito.verify(callback).failed(boom); Mockito.verify(callback, Mockito.never()).cancelled(); try { future.get(); } catch (final ExecutionException ex) { Assertions.assertSame(boom, ex.getCause()); } Assertions.assertTrue(future.isDone()); Assertions.assertFalse(future.isCancelled()); } @Test public void testCancelled() throws Exception { final FutureCallback callback = Mockito.mock(FutureCallback.class); final BasicFuture future = new BasicFuture<>(callback); final Object result = new Object(); final Exception boom = new Exception(); future.cancel(true); future.failed(boom); future.completed(result); Mockito.verify(callback, Mockito.never()).completed(Mockito.any()); Mockito.verify(callback, Mockito.never()).failed(Mockito.any()); Mockito.verify(callback).cancelled(); Assertions.assertThrows(CancellationException.class, future::get); Assertions.assertTrue(future.isDone()); Assertions.assertTrue(future.isCancelled()); } @Test public void testAsyncCompleted() throws Exception { final BasicFuture future = new BasicFuture<>(null); final Object result = new Object(); final Thread t = new Thread(() -> { try { Thread.sleep(100); future.completed(result); } catch (final InterruptedException boom) { } }); t.setDaemon(true); t.start(); Assertions.assertSame(result, future.get(60, TimeUnit.SECONDS)); Assertions.assertTrue(future.isDone()); Assertions.assertFalse(future.isCancelled()); } @Test public void testAsyncFailed() throws Exception { final BasicFuture future = new BasicFuture<>(null); final Exception boom = new Exception(); final Thread t = new Thread(() -> { try { Thread.sleep(100); future.failed(boom); } catch (final InterruptedException ex) { } }); t.setDaemon(true); t.start(); try { future.get(60, TimeUnit.SECONDS); } catch (final ExecutionException ex) { Assertions.assertSame(boom, ex.getCause()); } Assertions.assertTrue(future.isDone()); Assertions.assertFalse(future.isCancelled()); } @Test public void testAsyncCancelled() throws Exception { final BasicFuture future = new BasicFuture<>(null); final Thread t = new Thread(() -> { try { Thread.sleep(100); future.cancel(true); } catch (final InterruptedException ex) { } }); t.setDaemon(true); t.start(); Assertions.assertThrows(CancellationException.class, () -> future.get(60, TimeUnit.SECONDS)); } @Test public void testAsyncTimeout() throws Exception { final BasicFuture future = new BasicFuture<>(null); final Object result = new Object(); final Thread t = new Thread(() -> { try { Thread.sleep(200); future.completed(result); } catch (final InterruptedException ex) { } }); t.setDaemon(true); t.start(); Assertions.assertThrows(TimeoutException.class, () -> future.get(1, TimeUnit.MILLISECONDS)); } @Test public void testAsyncNegativeTimeout() throws Exception { final BasicFuture future = new BasicFuture<>(null); Assertions.assertThrows(TimeoutValueException.class, () -> future.get(-1, TimeUnit.MILLISECONDS)); } } httpcore5/src/test/java/org/apache/hc/core5/concurrent/TestCompletedFuture.java0100664 0000000 0000000 00000003271 14403631147 026634 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestCompletedFuture { @Test public void testCompleted() { final Object result = new Object(); final CompletedFuture future = new CompletedFuture<>(result); Assertions.assertSame(result, future.get()); Assertions.assertTrue(future.isDone()); Assertions.assertFalse(future.isCancelled()); future.cancel(true); future.cancel(); } }httpcore5/src/test/java/org/apache/hc/core5/concurrent/DefaultThreadFactoryTest.java0100664 0000000 0000000 00000003600 14403631147 027565 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.concurrent; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class DefaultThreadFactoryTest { @Test void newThread() throws Exception { final ThreadFactory defaultThreadFactory = new DefaultThreadFactory("I/O server dispatch", true); final CountDownLatch lockHeld = new CountDownLatch(1); final Thread thread = defaultThreadFactory.newThread(lockHeld::countDown); Assertions.assertNotNull(thread); thread.start(); Assertions.assertTrue(lockHeld.await(100, TimeUnit.MILLISECONDS)); } }httpcore5/pom.xml0100664 0000000 0000000 00000007552 14443065161 013056 0ustar000000000 0000000 4.0.0 org.apache.httpcomponents.core5 httpcore5-parent 5.2.2 httpcore5 Apache HttpComponents Core HTTP/1.1 2005 Apache HttpComponents HTTP/1.1 core components org.apache.httpcomponents.core5.httpcore5 org.junit.jupiter junit-jupiter test org.hamcrest hamcrest test org.mockito mockito-core test org.slf4j slf4j-api test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test src/main/resources true **/*.properties org.apache.maven.plugins maven-jar-plugin test-jar maven-project-info-reports-plugin false index dependencies dependency-info summary CODE_OF_CONDUCT.md0100664 0000000 0000000 00000001641 14323605260 012411 0ustar000000000 0000000 The Apache code of conduct page is [https://www.apache.org/foundation/policies/conduct.html](https://www.apache.org/foundation/policies/conduct.html). src/0040775 0000000 0000000 00000000000 14245617503 010410 5ustar000000000 0000000 src/site/0040775 0000000 0000000 00000000000 14245617503 011354 5ustar000000000 0000000 src/site/resources/0040775 0000000 0000000 00000000000 14245617503 013366 5ustar000000000 0000000 src/site/resources/css/0040775 0000000 0000000 00000000000 14245617503 014156 5ustar000000000 0000000 src/site/resources/css/site.css0100664 0000000 0000000 00000001514 14245617503 015632 0ustar000000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ @import url("/css/hc-maven.css"); src/site/site.xml0100664 0000000 0000000 00000006406 14245617503 013045 0ustar000000000 0000000 org.apache.httpcomponents maven-site-skin 1.1 Apache http://www.apache.org/images/asf_logo_wide.gif http://www.apache.org/ HttpComponents http://hc.apache.org/images/logos/httpcomponents.png http://hc.apache.org/
Apache HttpComponents, Commons HttpClient, Apache, the Apache feather logo, and the Apache HttpComponents project logo are trademarks of The Apache Software Foundation.

All other marks mentioned may be trademarks or registered trademarks of their respective owners.
NOTICE.txt0100664 0000000 0000000 00000000264 14403631147 011336 0ustar000000000 0000000 Apache HttpComponents Core Copyright 2005-2022 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). LICENSE.txt0100664 0000000 0000000 00000023677 14245617503 011460 0ustar000000000 0000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS httpcore5-testing/0040775 0000000 0000000 00000000000 14443065100 013177 5ustar000000000 0000000 httpcore5-testing/src/0040775 0000000 0000000 00000000000 14245617503 014000 5ustar000000000 0000000 httpcore5-testing/src/main/0040775 0000000 0000000 00000000000 14245617503 014724 5ustar000000000 0000000 httpcore5-testing/src/main/java/0040775 0000000 0000000 00000000000 14245617503 015645 5ustar000000000 0000000 httpcore5-testing/src/main/java/org/0040775 0000000 0000000 00000000000 14245617503 016434 5ustar000000000 0000000 httpcore5-testing/src/main/java/org/apache/0040775 0000000 0000000 00000000000 14245617503 017655 5ustar000000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/0040775 0000000 0000000 00000000000 14245617503 020247 5ustar000000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/0040775 0000000 0000000 00000000000 14245617503 021264 5ustar000000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/0040775 0000000 0000000 00000000000 14403631147 023212 5ustar000000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/package-info.java0100664 0000000 0000000 00000002376 14245617503 026412 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Apache Bench (AB) clone based on HttpCore. */ package org.apache.hc.core5.benchmark; httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/ResultFormatter.java0100664 0000000 0000000 00000007565 14245617503 027235 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.benchmark; import java.io.PrintStream; import java.text.NumberFormat; import java.util.Locale; public class ResultFormatter { private ResultFormatter() { // Do not allow utility class to be instantiated. } static final NumberFormat nf2 = NumberFormat.getInstance(Locale.ROOT); static final NumberFormat nf3 = NumberFormat.getInstance(Locale.ROOT); static final NumberFormat nf6 = NumberFormat.getInstance(Locale.ROOT); static { nf2.setMaximumFractionDigits(2); nf2.setMinimumFractionDigits(2); nf3.setMaximumFractionDigits(3); nf3.setMinimumFractionDigits(3); nf6.setMaximumFractionDigits(6); nf6.setMinimumFractionDigits(6); } public static void print(final PrintStream printStream, final Results results) { printStream.println("Server Software:\t\t" + results.getServerName()); printStream.println("Protocol version:\t\t" + results.getProtocolVersion()); printStream.println("Server Hostname:\t\t" + results.getHostName()); printStream.println("Server Port:\t\t\t" + results.getHostPort()); printStream.println("Document Path:\t\t\t" + results.getDocumentPath()); printStream.println("Document Length:\t\t" + results.getContentLength() + " bytes\n"); printStream.println("Concurrency Level:\t\t" + results.getConcurrencyLevel()); printStream.println("Time taken for tests:\t" + nf6.format((double) results.getTotalTimeMillis() / 1000) + " seconds"); printStream.println("Complete requests:\t\t" + results.getSuccessCount()); printStream.println("Failed requests:\t\t" + results.getFailureCount()); printStream.println("Kept alive:\t\t\t\t" + results.getKeepAliveCount()); printStream.println("Total transferred:\t\t" + results.getTotalBytesRcvd() + " bytes"); printStream.println("Content transferred:\t" + results.getTotalContentBytesRecvd() + " bytes"); printStream.println("Requests per second:\t" + nf2.format( results.getSuccessCount() / ((double) results.getTotalTimeMillis() / 1000)) + " [#/sec] (mean)"); printStream.println("Time per request:\t\t" + nf3.format( (double) results.getTotalTimeMillis() * results.getConcurrencyLevel() / results.getSuccessCount()) + " [ms] (mean)"); printStream.println("Time per request:\t\t" + nf3.format( (double) results.getTotalTimeMillis() / results.getSuccessCount()) + " [ms] (mean, across all concurrent requests)"); printStream.println("Transfer rate:\t\t\t" + nf2.format((double) results.getTotalBytesRcvd() / 1024 / ((double) results.getTotalTimeMillis() / 1000)) + " [Kbytes/sec] received"); } } httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Stats.java0100664 0000000 0000000 00000010277 14245617503 025163 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.benchmark; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.http.ProtocolVersion; /** * Statistics for an {@link HttpBenchmark HttpBenchmark}. * * @since 4.0 */ public class Stats { private final AtomicInteger successCount = new AtomicInteger(); private final AtomicInteger failureCount = new AtomicInteger(); private final AtomicInteger keepAliveCount = new AtomicInteger(); private final AtomicLong totalBytesRecv = new AtomicLong(); private final AtomicLong totalBytesSent = new AtomicLong(); private final AtomicLong contentLength = new AtomicLong(); private final AtomicLong totalContentLength = new AtomicLong(); private final AtomicReference serverNameRef = new AtomicReference<>(); private final AtomicReference versionRef = new AtomicReference<>(); public void incSuccessCount() { this.successCount.incrementAndGet(); } public int getSuccessCount() { return this.successCount.get(); } public void incFailureCount() { this.failureCount.incrementAndGet(); } public int getFailureCount() { return this.failureCount.get(); } public void incKeepAliveCount() { this.keepAliveCount.incrementAndGet(); } public int getKeepAliveCount() { return this.keepAliveCount.get(); } public void incTotalBytesRecv(final int n) { this.totalBytesRecv.addAndGet(n); } public long getTotalBytesRecv() { return this.totalBytesRecv.get(); } public void incTotalBytesSent(final int n) { this.totalBytesSent.addAndGet(n); } public long getTotalBytesSent() { return this.totalBytesSent.get(); } public void setContentLength(final long n) { this.contentLength.set(n); } public void incTotalContentLength(final long n) { this.totalContentLength.addAndGet(n); } public long getContentLength() { return this.contentLength.get(); } public long getTotalContentLength() { return this.totalContentLength.get(); } public void setServerName(final String serverName) { this.serverNameRef.set(serverName); } public String getServerName() { return this.serverNameRef.get(); } public ProtocolVersion getVersion() { return versionRef.get(); } public void setVersion(final ProtocolVersion version) { this.versionRef.set(version); } @Override public String toString() { return "Stats{" + "successCount=" + successCount + ", failureCount=" + failureCount + ", keepAliveCount=" + keepAliveCount + ", serverName=" + serverNameRef.get() + ", totalBytesRecv=" + totalBytesRecv + ", totalBytesSent=" + totalBytesSent + ", contentLength=" + contentLength + '}'; } } httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CommandLineUtils.java0100664 0000000 0000000 00000022055 14245617503 027271 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.benchmark; import java.io.File; import java.net.URI; import java.net.URISyntaxException; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; public class CommandLineUtils { private CommandLineUtils() { // Do not allow utility class to be instantiated. } public static Options getOptions() { final Option nopt = new Option("n", true, "Number of requests to perform. " + "The default is to just perform a single request which may lead " + "to non-representative benchmarking results"); nopt.setRequired(false); nopt.setArgName("requests"); final Option copt = new Option("c", true, "Number of multiple requests to make at a time. " + "The default is to just execute a single request"); copt.setRequired(false); copt.setArgName("concurrency"); final Option topt = new Option("t", true, "Seconds to max. to spend on benchmarking"); topt.setRequired(false); topt.setArgName("time-limit"); final Option sopt = new Option("s", true, "Seconds to max. wait for each response. Default is 60 seconds"); sopt.setRequired(false); sopt.setArgName("socket-Timeout"); final Option popt = new Option("p", true, "File containing data to enclose in the request"); popt.setRequired(false); popt.setArgName("Payload file"); final Option Topt = new Option("T", true, "Content-type header to use for enclosed request data"); Topt.setRequired(false); Topt.setArgName("content-type"); final Option vopt = new Option("v", true, "Set verbosity level: " + "1 prints warnings and errors, " + "2 prints response codes, " + "3 prints message headers, " + "4 prints HTTP/2 frame info, " + "5 prints HTTP/2 flow control events, " + "6 prints response content"); vopt.setRequired(false); vopt.setArgName("verbosity"); final Option iopt = new Option("i", false, "Use HEAD instead of GET"); iopt.setRequired(false); final Option Hopt = new Option("H", true, "Add arbitrary header line, " + "eg. 'Accept-Encoding: gzip' inserted after all normal " + "header lines. (repeatable as -H \"h1: v1\",\"h2: v2\" etc)"); Hopt.setRequired(false); Hopt.setArgName("header"); final Option kopt = new Option("k", false, "Use HTTP KeepAlive feature. Default is no KeepAlive"); kopt.setRequired(false); final Option mopt = new Option("m", true, "HTTP Method. Default is GET or POST if the request to enclose data"); mopt.setRequired(false); mopt.setArgName("HTTP method"); // HttpCore specific options final Option uopt = new Option("u", false, "Chunk entity. Default is false"); uopt.setRequired(false); final Option xopt = new Option("x", false, "Use Expect-Continue. Default is false"); xopt.setRequired(false); final Option gopt = new Option("g", false, "Accept GZip. Default is false"); gopt.setRequired(false); final Option http2opt = new Option("2", false, "Force HTTP/2"); gopt.setRequired(false); final Option hopt = new Option("h", false, "Display usage information"); nopt.setRequired(false); final Options options = new Options(); options.addOption(nopt); options.addOption(copt); options.addOption(topt); options.addOption(sopt); options.addOption(popt); options.addOption(Topt); options.addOption(vopt); options.addOption(iopt); options.addOption(Hopt); options.addOption(kopt); options.addOption(mopt); // HttpCore specific options options.addOption(uopt); options.addOption(xopt); options.addOption(gopt); options.addOption(http2opt); options.addOption(hopt); return options; } public static BenchmarkConfig parseCommandLine(final CommandLine cmd) { final BenchmarkConfig.Builder builder = new BenchmarkConfig.Builder(); if (cmd.hasOption('n')) { final String s = cmd.getOptionValue('n'); try { builder.setRequests(Integer.parseInt(s)); } catch (final NumberFormatException ex) { printError("Invalid number of requests: " + s); } } if (cmd.hasOption('c')) { final String s = cmd.getOptionValue('c'); try { builder.setConcurrencyLevel(Integer.parseInt(s)); } catch (final NumberFormatException ex) { printError("Invalid number for concurrency: " + s); } } if (cmd.hasOption('t')) { final String t = cmd.getOptionValue('t'); try { builder.setTimeLimit(TimeValue.ofSeconds(Integer.parseInt(t))); } catch (final NumberFormatException ex) { printError("Invalid time limit: " + t); } } if (cmd.hasOption('s')) { final String s = cmd.getOptionValue('s'); try { builder.setSocketTimeout(Timeout.ofMilliseconds(Integer.parseInt(s))); } catch (final NumberFormatException ex) { printError("Invalid socket timeout: " + s); } } if (cmd.hasOption('p')) { final File file = new File(cmd.getOptionValue('p')); if (!file.exists()) { printError("File not found: " + file); } builder.setPayloadFile(file); } if (cmd.hasOption('T')) { builder.setContentType(ContentType.parse(cmd.getOptionValue('T'))); } if (cmd.hasOption('v')) { final String s = cmd.getOptionValue('v'); try { builder.setVerbosity(Integer.parseInt(s)); } catch (final NumberFormatException ex) { printError("Invalid verbosity level: " + s); } } if (cmd.hasOption('i')) { builder.setHeadInsteadOfGet(true); } if (cmd.hasOption('H')) { final String headerStr = cmd.getOptionValue('H'); builder.setHeaders(headerStr.split(",")); } if (cmd.hasOption('k')) { builder.setKeepAlive(true); } if (cmd.hasOption('m')) { builder.setMethod(cmd.getOptionValue('m')); } else if (cmd.hasOption('p')) { builder.setMethod(Method.POST.name()); } if (cmd.hasOption('u')) { builder.setUseChunking(true); } if (cmd.hasOption('x')) { builder.setUseExpectContinue(true); } if (cmd.hasOption('g')) { builder.setUseAcceptGZip(true); } if (cmd.hasOption('2')) { builder.setForceHttp2(true); } final String[] cmdargs = cmd.getArgs(); if (cmdargs.length > 0) { try { builder.setUri(new URI(cmdargs[0])); } catch (final URISyntaxException e) { printError("Invalid request URI: " + cmdargs[0]); } } return builder.build(); } static void showUsage(final Options options) { final HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("HttpBenchmark [options] [http://]hostname[:port]/path?query", options); } static void printError(final String msg) { System.err.println(msg); showUsage(getOptions()); System.exit(-1); } } httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/BenchmarkConfig.java0100664 0000000 0000000 00000035146 14245617503 027107 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.benchmark; import java.io.File; import java.net.URI; import java.util.Arrays; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; public class BenchmarkConfig { private final URI uri; private final int requests; private final int concurrencyLevel; private final TimeValue timeLimit; private final Timeout socketTimeout; private final File payloadFile; private final ContentType contentType; private final int verbosity; private final boolean headInsteadOfGet; private final String[] headers; private final boolean keepAlive; private final String method; private final boolean useChunking; private final boolean useExpectContinue; private final boolean useAcceptGZip; private final String payloadText; private final String soapAction; private final boolean forceHttp2; private final boolean disableSSLVerification; private final String trustStorePath; private final String identityStorePath; private final String trustStorePassword; private final String identityStorePassword; private BenchmarkConfig(final URI uri, final int requests, final int concurrencyLevel, final TimeValue timeLimit, final Timeout socketTimeout, final File payloadFile, final ContentType contentType, final int verbosity, final boolean headInsteadOfGet, final String[] headers, final boolean keepAlive, final String method, final boolean useChunking, final boolean useExpectContinue, final boolean useAcceptGZip, final String payloadText, final String soapAction, final boolean forceHttp2, final boolean disableSSLVerification, final String trustStorePath, final String identityStorePath, final String trustStorePassword, final String identityStorePassword) { this.uri = uri; this.requests = requests; this.concurrencyLevel = concurrencyLevel; this.timeLimit = timeLimit; this.socketTimeout = socketTimeout; this.payloadFile = payloadFile; this.contentType = contentType; this.verbosity = verbosity; this.headInsteadOfGet = headInsteadOfGet; this.headers = headers; this.keepAlive = keepAlive; this.method = method; this.useChunking = useChunking; this.useExpectContinue = useExpectContinue; this.useAcceptGZip = useAcceptGZip; this.payloadText = payloadText; this.soapAction = soapAction; this.forceHttp2 = forceHttp2; this.disableSSLVerification = disableSSLVerification; this.trustStorePath = trustStorePath; this.identityStorePath = identityStorePath; this.trustStorePassword = trustStorePassword; this.identityStorePassword = identityStorePassword; } public URI getUri() { return uri; } public int getRequests() { return requests; } public int getConcurrencyLevel() { return concurrencyLevel; } public boolean isKeepAlive() { return keepAlive; } public int getVerbosity() { return verbosity; } public boolean isHeadInsteadOfGet() { return headInsteadOfGet; } public File getPayloadFile() { return payloadFile; } public ContentType getContentType() { return contentType; } public String[] getHeaders() { return headers != null ? headers.clone() : null; } public Timeout getSocketTimeout() { return socketTimeout; } public String getMethod() { return method; } public boolean isUseChunking() { return useChunking; } public boolean isUseExpectContinue() { return useExpectContinue; } public boolean isUseAcceptGZip() { return useAcceptGZip; } public String getPayloadText() { return payloadText; } public String getSoapAction() { return soapAction; } public boolean isForceHttp2() { return forceHttp2; } public boolean isDisableSSLVerification() { return disableSSLVerification; } public String getTrustStorePath() { return trustStorePath; } public String getIdentityStorePath() { return identityStorePath; } public String getTrustStorePassword() { return trustStorePassword; } public String getIdentityStorePassword() { return identityStorePassword; } public TimeValue getTimeLimit() { return timeLimit; } @Override public String toString() { return "[" + "uri=" + uri + ", requests=" + requests + ", concurrencyLevel=" + concurrencyLevel + ", timeLimit=" + timeLimit + ", socketTimeout=" + socketTimeout + ", payloadFile=" + payloadFile + ", contentType=" + contentType + ", verbosity=" + verbosity + ", headInsteadOfGet=" + headInsteadOfGet + ", headers=" + Arrays.toString(headers) + ", keepAlive=" + keepAlive + ", method='" + method + '\'' + ", useChunking=" + useChunking + ", useExpectContinue=" + useExpectContinue + ", useAcceptGZip=" + useAcceptGZip + ", payloadText='" + payloadText + '\'' + ", soapAction='" + soapAction + '\'' + ", forceHttp2=" + forceHttp2+ ", disableSSLVerification=" + disableSSLVerification + ", trustStorePath='" + trustStorePath + '\'' + ", identityStorePath='" + identityStorePath + '\'' + ", trustStorePassword='" + trustStorePassword + '\'' + ", identityStorePassword='" + identityStorePassword + '\'' + ']'; } public static BenchmarkConfig.Builder custom() { return new BenchmarkConfig.Builder(); } public static BenchmarkConfig.Builder copy(final BenchmarkConfig config) { Args.notNull(config, "Socket config"); return new Builder() .setUri(config.getUri()) .setRequests(config.getRequests()) .setConcurrencyLevel(config.getConcurrencyLevel()) .setTimeLimit(config.getTimeLimit()) .setSocketTimeout(config.getSocketTimeout()) .setPayloadFile(config.getPayloadFile()) .setContentType(config.getContentType()) .setVerbosity(config.getVerbosity()) .setHeadInsteadOfGet(config.isHeadInsteadOfGet()) .setHeaders(config.getHeaders()) .setKeepAlive(config.isKeepAlive()) .setMethod(config.getMethod()) .setUseChunking(config.isUseChunking()) .setUseExpectContinue(config.isUseExpectContinue()) .setUseAcceptGZip(config.isUseAcceptGZip()) .setPayloadText(config.getPayloadText()) .setSoapAction(config.getSoapAction()) .setForceHttp2(config.isForceHttp2()) .setDisableSSLVerification(config.isDisableSSLVerification()) .setTrustStorePath(config.getTrustStorePath()) .setIdentityStorePath(config.getIdentityStorePath()) .setTrustStorePassword(config.getTrustStorePassword()) .setIdentityStorePassword(config.getIdentityStorePassword()); } public static class Builder { private URI uri; private int requests; private int concurrencyLevel; private TimeValue timeLimit; private Timeout socketTimeout; private File payloadFile; private ContentType contentType; private int verbosity; private boolean headInsteadOfGet; private String[] headers; private boolean keepAlive; private String method; private boolean useChunking; private boolean useExpectContinue; private boolean useAcceptGZip; private String payloadText; private String soapAction; private boolean forceHttp2; private boolean disableSSLVerification; private String trustStorePath; private String identityStorePath; private String trustStorePassword; private String identityStorePassword; public Builder() { super(); this.requests = 1; this.concurrencyLevel = 1; this.keepAlive = false; this.verbosity = 0; this.headInsteadOfGet = false; this.socketTimeout = Timeout.ofSeconds(60); } public Builder setUri(final URI uri) { this.uri = uri; return this; } public Builder setRequests(final int requests) { this.requests = requests; return this; } public Builder setConcurrencyLevel(final int concurrencyLevel) { this.concurrencyLevel = concurrencyLevel; return this; } public Builder setKeepAlive(final boolean keepAlive) { this.keepAlive = keepAlive; return this; } public Builder setVerbosity(final int verbosity) { this.verbosity = verbosity; return this; } public Builder setHeadInsteadOfGet(final boolean headInsteadOfGet) { this.headInsteadOfGet = headInsteadOfGet; return this; } public Builder setContentType(final ContentType contentType) { this.contentType = contentType; return this; } public Builder setHeaders(final String[] headers) { this.headers = headers; return this; } public Builder setSocketTimeout(final Timeout socketTimeout) { this.socketTimeout = socketTimeout; return this; } public Builder setMethod(final String method) { this.method = method; return this; } public Builder setUseChunking(final boolean useChunking) { this.useChunking = useChunking; return this; } public Builder setUseExpectContinue(final boolean useExpectContinue) { this.useExpectContinue = useExpectContinue; return this; } public Builder setUseAcceptGZip(final boolean useAcceptGZip) { this.useAcceptGZip = useAcceptGZip; return this; } public Builder setPayloadFile(final File payloadFile) { this.payloadFile = payloadFile; return this; } public Builder setPayloadText(final String payloadText) { this.payloadText = payloadText; return this; } public Builder setSoapAction(final String soapAction) { this.soapAction = soapAction; return this; } public Builder setTimeLimit(final TimeValue timeLimit) { this.timeLimit = timeLimit; return this; } public Builder setForceHttp2(final boolean forceHttp2) { this.forceHttp2 = forceHttp2; return this; } public Builder setDisableSSLVerification(final boolean disableSSLVerification) { this.disableSSLVerification = disableSSLVerification; return this; } public Builder setTrustStorePath(final String trustStorePath) { this.trustStorePath = trustStorePath; return this; } public Builder setIdentityStorePath(final String identityStorePath) { this.identityStorePath = identityStorePath; return this; } public Builder setTrustStorePassword(final String trustStorePassword) { this.trustStorePassword = trustStorePassword; return this; } public Builder setIdentityStorePassword(final String identityStorePassword) { this.identityStorePassword = identityStorePassword; return this; } public BenchmarkConfig build() { return new BenchmarkConfig( uri, requests, concurrencyLevel, timeLimit, socketTimeout, payloadFile, contentType, verbosity, headInsteadOfGet, headers, keepAlive, method, useChunking, useExpectContinue, useAcceptGZip, payloadText, soapAction, forceHttp2, disableSSLVerification, trustStorePath, identityStorePath, trustStorePassword, identityStorePassword); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Results.java0100664 0000000 0000000 00000012113 14245617503 025515 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.benchmark; import org.apache.hc.core5.http.ProtocolVersion; /** * Benchmark results * * @since 4.3 */ public final class Results { private final String serverName; private final ProtocolVersion protocolVersion; private final String hostName; private final int hostPort; private final String documentPath; private final long contentLength; private final int concurrencyLevel; private final long totalTimeMillis; private final long successCount; private final long failureCount; private final long keepAliveCount; private final long totalBytesRcvd; private final long totalBytesSent; private final long totalContentBytesRecvd; public Results( final String serverName, final ProtocolVersion protocolVersion, final String hostName, final int hostPort, final String documentPath, final long contentLength, final int concurrencyLevel, final long totalTimeMillis, final long successCount, final long failureCount, final long keepAliveCount, final long totalBytesRcvd, final long totalBytesSent, final long totalContentBytesRecvd) { this.serverName = serverName; this.protocolVersion = protocolVersion; this.hostName = hostName; this.hostPort = hostPort; this.documentPath = documentPath; this.contentLength = contentLength; this.concurrencyLevel = concurrencyLevel; this.totalTimeMillis = totalTimeMillis; this.successCount = successCount; this.failureCount = failureCount; this.keepAliveCount = keepAliveCount; this.totalBytesRcvd = totalBytesRcvd; this.totalBytesSent = totalBytesSent; this.totalContentBytesRecvd = totalContentBytesRecvd; } public String getServerName() { return serverName; } public ProtocolVersion getProtocolVersion() { return protocolVersion; } public String getHostName() { return hostName; } public int getHostPort() { return hostPort; } public String getDocumentPath() { return documentPath; } public long getContentLength() { return contentLength; } public int getConcurrencyLevel() { return concurrencyLevel; } public long getTotalTimeMillis() { return totalTimeMillis; } public long getSuccessCount() { return successCount; } public long getFailureCount() { return failureCount; } public long getKeepAliveCount() { return keepAliveCount; } public long getTotalBytesRcvd() { return totalBytesRcvd; } public long getTotalBytesSent() { return totalBytesSent; } public long getTotalContentBytesRecvd() { return totalContentBytesRecvd; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("[serverName=").append(serverName) .append(", hostName=").append(hostName) .append(", hostPort=").append(hostPort) .append(", documentPath=").append(documentPath) .append(", contentLength=").append(contentLength) .append(", concurrencyLevel=").append(concurrencyLevel) .append(", totalTimeMillis=").append(totalTimeMillis) .append(", successCount=").append(successCount) .append(", failureCount=").append(failureCount) .append(", keepAliveCount=").append(keepAliveCount) .append(", totalBytesRcvd=").append(totalBytesRcvd) .append(", totalBytesSent=").append(totalBytesSent) .append(", totalContentBytesRecvd=").append(totalContentBytesRecvd) .append("]"); return builder.toString(); } } httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/HttpBenchmark.java0100664 0000000 0000000 00000050125 14403631147 026607 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.benchmark; import java.io.File; import java.io.IOException; import java.net.SocketAddress; import java.net.URI; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.file.Paths; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import javax.net.ssl.SSLContext; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.nio.ssl.BasicClientTlsStrategy; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessorBuilder; import org.apache.hc.core5.http.protocol.RequestExpectContinue; import org.apache.hc.core5.http.protocol.RequestUserAgent; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.FramePrinter; import org.apache.hc.core5.http2.frame.FrameType; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap; import org.apache.hc.core5.http2.protocol.H2RequestConnControl; import org.apache.hc.core5.http2.protocol.H2RequestContent; import org.apache.hc.core5.http2.protocol.H2RequestTargetHost; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.ssl.SSLContextBuilder; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.Timeout; /** * Main program of the HTTP benchmark. * * @since 4.0 */ public class HttpBenchmark { private final BenchmarkConfig config; public static void main(final String[] args) throws Exception { final Options options = CommandLineUtils.getOptions(); final CommandLineParser parser = new DefaultParser(); final CommandLine cmd = parser.parse(options, args); if (args.length == 0 || cmd.hasOption('h') || cmd.getArgs().length != 1) { CommandLineUtils.showUsage(options); System.exit(1); } final BenchmarkConfig config = CommandLineUtils.parseCommandLine(cmd); if (config.getUri() == null) { CommandLineUtils.showUsage(options); System.exit(1); } final HttpBenchmark httpBenchmark = new HttpBenchmark(config); final Results results = httpBenchmark.execute(); System.out.println(); ResultFormatter.print(System.out, results); } public HttpBenchmark(final BenchmarkConfig config) { super(); this.config = config != null ? config : BenchmarkConfig.custom().build(); } public Results execute() throws Exception { final HttpProcessorBuilder builder = HttpProcessorBuilder.create() .addAll( H2RequestContent.INSTANCE, H2RequestTargetHost.INSTANCE, H2RequestConnControl.INSTANCE, new RequestUserAgent("HttpCore-AB/5.0")); if (this.config.isUseExpectContinue()) { builder.add(RequestExpectContinue.INSTANCE); } final SSLContext sslContext; if ("https".equals(config.getUri().getScheme())) { final SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); sslContextBuilder.setProtocol("SSL"); if (config.isDisableSSLVerification()) { sslContextBuilder.loadTrustMaterial(null, (chain, authType) -> true); } else if (config.getTrustStorePath() != null) { sslContextBuilder.loadTrustMaterial( new File(config.getTrustStorePath()), config.getTrustStorePassword() != null ? config.getTrustStorePassword().toCharArray() : null); } if (config.getIdentityStorePath() != null) { sslContextBuilder.loadKeyMaterial( Paths.get(config.getIdentityStorePath()), config.getIdentityStorePassword() != null ? config.getIdentityStorePassword().toCharArray() : null, config.getIdentityStorePassword() != null ? config.getIdentityStorePassword().toCharArray() : null); } sslContext = sslContextBuilder.build(); } else { sslContext = SSLContexts.createSystemDefault(); } final HttpVersionPolicy versionPolicy; if (config.isForceHttp2()) { versionPolicy = HttpVersionPolicy.FORCE_HTTP_2; } else { if (sslContext != null) { versionPolicy = HttpVersionPolicy.NEGOTIATE; } else { versionPolicy = HttpVersionPolicy.FORCE_HTTP_1; } } final Stats stats = new Stats(); try (final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap() .setHttpProcessor(builder.build()) .setTlsStrategy(new BasicClientTlsStrategy(sslContext)) .setVersionPolicy(versionPolicy) .setH2Config(H2Config.custom() .setPushEnabled(false) .build()) .setIOSessionDecorator(ioSession -> new IOSession() { @Override public String getId() { return ioSession.getId(); } @Override public Lock getLock() { return ioSession.getLock(); } @Override public void enqueue(final Command command, final Command.Priority priority) { ioSession.enqueue(command, priority); } @Override public boolean hasCommands() { return ioSession.hasCommands(); } @Override public Command poll() { return ioSession.poll(); } @Override public ByteChannel channel() { return ioSession.channel(); } @Override public SocketAddress getRemoteAddress() { return ioSession.getRemoteAddress(); } @Override public SocketAddress getLocalAddress() { return ioSession.getLocalAddress(); } @Override public int getEventMask() { return ioSession.getEventMask(); } @Override public void setEventMask(final int ops) { ioSession.setEventMask(ops); } @Override public void setEvent(final int op) { ioSession.setEvent(op); } @Override public void clearEvent(final int op) { ioSession.clearEvent(op); } @Override public void close() { ioSession.close(); } @Override public Status getStatus() { return ioSession.getStatus(); } @Override public int read(final ByteBuffer dst) throws IOException { final int bytesRead = ioSession.read(dst); if (bytesRead > 0) { stats.incTotalBytesRecv(bytesRead); } return bytesRead; } @Override public int write(final ByteBuffer src) throws IOException { final int bytesWritten = ioSession.write(src); if (bytesWritten > 0) { stats.incTotalBytesSent(bytesWritten); } return bytesWritten; } @Override public boolean isOpen() { return ioSession.isOpen(); } @Override public Timeout getSocketTimeout() { return ioSession.getSocketTimeout(); } @Override public void setSocketTimeout(final Timeout timeout) { ioSession.setSocketTimeout(timeout); } @Override public long getLastReadTime() { return ioSession.getLastReadTime(); } @Override public long getLastWriteTime() { return ioSession.getLastWriteTime(); } @Override public long getLastEventTime() { return ioSession.getLastEventTime(); } @Override public void updateReadTime() { ioSession.updateReadTime(); } @Override public void updateWriteTime() { ioSession.updateWriteTime(); } @Override public void close(final CloseMode closeMode) { ioSession.close(closeMode); } @Override public IOEventHandler getHandler() { return ioSession.getHandler(); } @Override public void upgrade(final IOEventHandler handler) { ioSession.upgrade(handler); } }) .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { if (config.getVerbosity() >= 3) { System.out.println(">> " + request.getMethod() + " " + request.getRequestUri()); final Header[] headers = request.getHeaders(); for (final Header header : headers) { System.out.println(">> " + header); } System.out.println(); } } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { if (config.getVerbosity() >= 3) { System.out.println("<< " + response.getCode() + " " + response.getReasonPhrase()); final Header[] headers = response.getHeaders(); for (final Header header : headers) { System.out.println("<< " + header); } System.out.println(); } } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { } }) .setStreamListener(new H2StreamListener() { private final FramePrinter framePrinter = new FramePrinter(); @Override public void onHeaderInput( final HttpConnection connection, final int streamId, final List headers) { if (config.getVerbosity() >= 3) { for (final Header header : headers) { System.out.println("<< " + header); } System.out.println(); } } @Override public void onHeaderOutput( final HttpConnection connection, final int streamId, final List headers) { if (config.getVerbosity() >= 3) { for (final Header header : headers) { System.out.println(">> " + header); } System.out.println(); } } @Override public void onFrameInput( final HttpConnection connection, final int streamId, final RawFrame frame) { if (config.getVerbosity() >= 4) { System.out.print("<< "); try { framePrinter.printFrameInfo(frame, System.out); System.out.println(); if (!frame.isType(FrameType.DATA)) { framePrinter.printPayload(frame, System.out); System.out.println(); } } catch (final IOException ignore) { } } } @Override public void onFrameOutput( final HttpConnection connection, final int streamId, final RawFrame frame) { if (config.getVerbosity() >= 4) { System.out.print(">> "); try { framePrinter.printFrameInfo(frame, System.out); System.out.println(); if (!frame.isType(FrameType.DATA)) { framePrinter.printPayload(frame, System.out); System.out.println(); } } catch (final IOException ignore) { } } } @Override public void onInputFlowControl( final HttpConnection connection, final int streamId, final int delta, final int actualSize) { if (config.getVerbosity() >= 5) { System.out.println("<< stream " + streamId + ": " + actualSize + " " + delta); } } @Override public void onOutputFlowControl( final HttpConnection connection, final int streamId, final int delta, final int actualSize) { if (config.getVerbosity() >= 5) { System.out.println(">> stream " + streamId + ": " + actualSize + " " + delta); } } }) .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(config.getSocketTimeout()) .build()) .create()) { requester.setDefaultMaxPerRoute(config.getConcurrencyLevel()); requester.setMaxTotal(config.getConcurrencyLevel() * 2); requester.start(); return doExecute(requester, stats); } } private Results doExecute(final HttpAsyncRequester requester, final Stats stats) throws Exception { final URI requestUri = config.getUri(); final HttpHost host = new HttpHost(requestUri.getScheme(), requestUri.getHost(), requestUri.getPort()); final AtomicLong requestCount = new AtomicLong(config.getRequests()); final HttpVersion version = HttpVersion.HTTP_1_1; final CountDownLatch completionLatch = new CountDownLatch(config.getConcurrencyLevel()); final BenchmarkWorker[] workers = new BenchmarkWorker[config.getConcurrencyLevel()]; for (int i = 0; i < workers.length; i++) { final HttpCoreContext context = HttpCoreContext.create(); context.setProtocolVersion(version); final BenchmarkWorker worker = new BenchmarkWorker( requester, host, context, requestCount, completionLatch, stats, config); workers[i] = worker; } final long deadline = config.getTimeLimit() != null ? config.getTimeLimit().toMilliseconds() : Long.MAX_VALUE; final long startTime = System.currentTimeMillis(); for (int i = 0; i < workers.length; i++) { workers[i].execute(); } completionLatch.await(deadline, TimeUnit.MILLISECONDS); if (config.getVerbosity() >= 3) { System.out.println("...done"); } final long endTime = System.currentTimeMillis(); for (int i = 0; i < workers.length; i++) { workers[i].releaseResources(); } return new Results( stats.getServerName(), stats.getVersion(), host.getHostName(), host.getPort() > 0 ? host.getPort() : host.getSchemeName().equalsIgnoreCase("https") ? 443 : 80, requestUri.toASCIIString(), stats.getContentLength(), config.getConcurrencyLevel(), endTime - startTime, stats.getSuccessCount(), stats.getFailureCount(), stats.getKeepAliveCount(), stats.getTotalBytesRecv(), stats.getTotalBytesSent(), stats.getTotalContentLength()); } } httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/BenchmarkWorker.java0100664 0000000 0000000 00000033730 14403631147 027144 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.benchmark; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.nio.ResourceHolder; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer; import org.apache.hc.core5.http.nio.entity.FileEntityProducer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; class BenchmarkWorker implements ResourceHolder { private final HttpAsyncRequester requester; private final HttpHost host; private final HttpCoreContext context; private final AtomicLong requestCount; private final CountDownLatch completionLatch; private final Stats stats; private final BenchmarkConfig config; private final AtomicReference endpointRef; public BenchmarkWorker( final HttpAsyncRequester requester, final HttpHost host, final HttpCoreContext context, final AtomicLong requestCount, final CountDownLatch completionLatch, final Stats stats, final BenchmarkConfig config) { this.requester = requester; this.host = host; this.context = context; this.requestCount = requestCount; this.completionLatch = completionLatch; this.stats = stats; this.config = config; this.endpointRef = new AtomicReference<>(); } private AsyncRequestProducer createRequestProducer() { String method = config.getMethod(); if (method == null) { method = config.isHeadInsteadOfGet() ? Method.HEAD.name() : Method.GET.name(); } final BasicHttpRequest request = new BasicHttpRequest(method, config.getUri()); final String[] headers = config.getHeaders(); if (headers != null) { for (final String s : headers) { final int pos = s.indexOf(':'); if (pos != -1) { request.addHeader(new BasicHeader(s.substring(0, pos).trim(), s.substring(pos + 1))); } } } if (!config.isKeepAlive() && !config.isForceHttp2()) { request.addHeader(new BasicHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE)); } if (config.isUseAcceptGZip()) { request.addHeader(new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "gzip")); } if (config.getSoapAction() != null && config.getSoapAction().length() > 0) { request.addHeader(new BasicHeader("SOAPAction", config.getSoapAction())); } final AsyncEntityProducer entityProducer; if (config.getPayloadFile() != null) { entityProducer = new FileEntityProducer( config.getPayloadFile(), config.getContentType(), config.isUseChunking()); } else if (config.getPayloadText() != null) { entityProducer = new BasicAsyncEntityProducer( config.getPayloadText(), config.getContentType(), config.isUseChunking()); } else { entityProducer = null; } return new AsyncRequestProducer() { @Override public void sendRequest( final RequestChannel channel, final HttpContext context) throws HttpException, IOException { channel.sendRequest(request, entityProducer, context); } @Override public boolean isRepeatable() { return entityProducer == null || entityProducer.isRepeatable(); } @Override public int available() { return entityProducer != null ? entityProducer.available() : 0; } @Override public void produce(final DataStreamChannel channel) throws IOException { if (entityProducer != null) { entityProducer.produce(channel); } } @Override public void failed(final Exception cause) { if (config.getVerbosity() >= 1) { System.out.println("Failed HTTP request: " + cause.getMessage()); } } @Override public void releaseResources() { if (entityProducer != null) { entityProducer.releaseResources(); } } }; } private AsyncResponseConsumer createResponseConsumer() { return new AsyncResponseConsumer() { volatile int status; volatile Charset charset; final AtomicLong contentLength = new AtomicLong(); final AtomicReference> resultCallbackRef = new AtomicReference<>(); @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context, final FutureCallback resultCallback) throws HttpException, IOException { status = response.getCode(); resultCallbackRef.set(resultCallback); stats.setVersion(response.getVersion()); final Header serverHeader = response.getFirstHeader(HttpHeaders.SERVER); if (serverHeader != null) { stats.setServerName(serverHeader.getValue()); } if (config.getVerbosity() >= 2) { System.out.println(response.getCode()); } if (entityDetails != null) { if (config.getVerbosity() >= 6) { if (entityDetails.getContentType() != null) { final ContentType contentType = ContentType.parseLenient(entityDetails.getContentType()); charset = ContentType.getCharset(contentType, null); } } } else { streamEnd(null); } } @Override public void informationResponse( final HttpResponse response, final HttpContext context) throws HttpException, IOException { } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(Integer.MAX_VALUE); } @Override public void consume(final ByteBuffer src) throws IOException { final int n = src.remaining(); contentLength.addAndGet(n); stats.incTotalContentLength(n); if (config.getVerbosity() >= 6) { final CharsetDecoder decoder = (charset != null ? charset : StandardCharsets.US_ASCII).newDecoder(); System.out.print(decoder.decode(src)); } } @Override public void streamEnd(final List trailers) throws HttpException, IOException { if (status == HttpStatus.SC_OK) { stats.incSuccessCount(); } else { stats.incFailureCount(); } stats.setContentLength(contentLength.get()); final FutureCallback resultCallback = resultCallbackRef.getAndSet(null); if (resultCallback != null) { resultCallback.completed(null); } if (config.getVerbosity() >= 6) { System.out.println(); System.out.println(); } } @Override public void failed(final Exception cause) { stats.incFailureCount(); final FutureCallback resultCallback = resultCallbackRef.getAndSet(null); if (resultCallback != null) { resultCallback.failed(cause); } if (config.getVerbosity() >= 1) { System.out.println("HTTP response error: " + cause.getMessage()); } } @Override public void releaseResources() { } }; } public void execute() { if (requestCount.decrementAndGet() >= 0) { AsyncClientEndpoint endpoint = endpointRef.get(); if (endpoint != null && !endpoint.isConnected()) { endpoint.releaseAndDiscard(); endpoint = null; } if (endpoint == null) { requester.connect(host, config.getSocketTimeout(), null, new FutureCallback() { @Override public void completed(final AsyncClientEndpoint endpoint) { endpointRef.set(endpoint); endpoint.execute( createRequestProducer(), createResponseConsumer(), context, new FutureCallback() { @Override public void completed(final Void result) { execute(); } @Override public void failed(final Exception cause) { execute(); } @Override public void cancelled() { completionLatch.countDown(); } }); } @Override public void failed(final Exception cause) { stats.incFailureCount(); if (config.getVerbosity() >= 1) { System.out.println("Connect error: " + cause.getMessage()); } execute(); } @Override public void cancelled() { completionLatch.countDown(); } }); } else { stats.incKeepAliveCount(); endpoint.execute( createRequestProducer(), createResponseConsumer(), context, new FutureCallback() { @Override public void completed(final Void result) { execute(); } @Override public void failed(final Exception cause) { execute(); } @Override public void cancelled() { completionLatch.countDown(); } }); } } else { completionLatch.countDown(); } } @Override public void releaseResources() { final AsyncClientEndpoint endpoint = endpointRef.getAndSet(null); if (endpoint != null) { endpoint.releaseAndDiscard(); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/0040775 0000000 0000000 00000000000 14403631147 022735 5ustar000000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/SocksProxy.java0100664 0000000 0000000 00000026244 14403631147 025731 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.net.InetAddressUtils; import org.apache.hc.core5.util.TimeValue; /** * Cheap and nasty SOCKS protocol version 5 proxy, recommended for use in unit tests only so we can test our SOCKS client code. */ public class SocksProxy { private static class SocksProxyHandler { public static final int VERSION_5 = 5; public static final int COMMAND_CONNECT = 1; public static final int ATYP_DOMAINNAME = 3; private final SocksProxy parent; private final Socket socket; private volatile Socket remote; public SocksProxyHandler(final SocksProxy parent, final Socket socket) { this.parent = parent; this.socket = socket; } public void start() { new Thread(new Runnable() { @Override public void run() { try { final DataInputStream input = new DataInputStream(socket.getInputStream()); final DataOutputStream output = new DataOutputStream(socket.getOutputStream()); final Socket target = establishConnection(input, output); remote = target; final Thread t1 = pumpStream(input, target.getOutputStream()); final Thread t2 = pumpStream(target.getInputStream(), output); try { t1.join(); } catch (final InterruptedException e) { } try { t2.join(); } catch (final InterruptedException e) { } } catch (final IOException e) { } finally { parent.cleanupSocksProxyHandler(SocksProxyHandler.this); } } private Socket establishConnection(final DataInputStream input, final DataOutputStream output) throws IOException { final int clientVersion = input.readUnsignedByte(); if (clientVersion != VERSION_5) { throw new IOException("SOCKS implementation only supports version 5"); } final int nMethods = input.readUnsignedByte(); for (int i = 0; i < nMethods; i++) { input.readUnsignedByte(); // auth method } // response output.writeByte(VERSION_5); output.writeByte(0); // no auth method output.flush(); input.readUnsignedByte(); // client version again final int command = input.readUnsignedByte(); if (command != COMMAND_CONNECT) { throw new IOException("SOCKS implementation only supports CONNECT command"); } input.readUnsignedByte(); // reserved final String targetHost; final byte[] targetAddress; final int addressType = input.readUnsignedByte(); switch (addressType) { case InetAddressUtils.IPV4: targetHost = null; targetAddress = new byte[4]; for (int i = 0; i < targetAddress.length; i++) { targetAddress[i] = input.readByte(); } break; case InetAddressUtils.IPV6: targetHost = null; targetAddress = new byte[16]; for (int i = 0; i < targetAddress.length; i++) { targetAddress[i] = input.readByte(); } break; case ATYP_DOMAINNAME: final int length = input.readUnsignedByte(); final StringBuilder domainname = new StringBuilder(); for (int i = 0; i < length; i++) { domainname.append((char) input.readUnsignedByte()); } targetHost = domainname.toString(); targetAddress = null; break; default: throw new IOException("Unsupported address type: " + addressType); } final int targetPort = input.readUnsignedShort(); final Socket target; if (targetHost != null) { target = new Socket(targetHost, targetPort); } else { target = new Socket(InetAddress.getByAddress(targetAddress), targetPort); } output.writeByte(VERSION_5); output.writeByte(0); /* success */ output.writeByte(0); /* reserved */ final byte[] localAddress = target.getLocalAddress().getAddress(); if (localAddress.length == 4) { output.writeByte(InetAddressUtils.IPV4); } else if (localAddress.length == 16) { output.writeByte(InetAddressUtils.IPV6); } else { throw new IOException("Unsupported localAddress byte length: " + localAddress.length); } output.write(localAddress); output.writeShort(target.getLocalPort()); output.flush(); return target; } private Thread pumpStream(final InputStream input, final OutputStream output) { final Thread t = new Thread(() -> { final byte[] buffer = new byte[1024 * 8]; try { while (true) { final int read = input.read(buffer); if (read < 0) { break; } output.write(buffer, 0, read); output.flush(); } } catch (final IOException e) { } finally { shutdown(); } }); t.start(); return t; } }).start(); } public void shutdown() { try { this.socket.close(); } catch (final IOException e) { } if (this.remote != null) { try { this.remote.close(); } catch (final IOException e) { } } } } private final int port; private final List handlers = new ArrayList<>(); private ServerSocket server; private Thread serverThread; public SocksProxy() { this(0); } public SocksProxy(final int port) { this.port = port; } public synchronized void start() throws IOException { if (this.server == null) { this.server = new ServerSocket(this.port); this.serverThread = new Thread(() -> { try { while (true) { final Socket socket = server.accept(); startSocksProxyHandler(socket); } } catch (final IOException e) { } finally { if (server != null) { try { server.close(); } catch (final IOException e) { } server = null; } } }); this.serverThread.start(); } } public void shutdown(final TimeValue timeout) throws InterruptedException { final long waitUntil = System.currentTimeMillis() + timeout.toMilliseconds(); Thread t = null; synchronized (this) { if (this.server != null) { try { this.server.close(); } catch (final IOException e) { } finally { this.server = null; } t = this.serverThread; this.serverThread = null; } for (final SocksProxyHandler handler : this.handlers) { handler.shutdown(); } while (!this.handlers.isEmpty()) { final long waitTime = waitUntil - System.currentTimeMillis(); if (waitTime > 0) { wait(waitTime); } } } if (t != null) { final long waitTime = waitUntil - System.currentTimeMillis(); if (waitTime > 0) { t.join(waitTime); } } } protected void startSocksProxyHandler(final Socket socket) { final SocksProxyHandler handler = new SocksProxyHandler(this, socket); synchronized (this) { this.handlers.add(handler); } handler.start(); } protected synchronized void cleanupSocksProxyHandler(final SocksProxyHandler handler) { this.handlers.remove(handler); } public SocketAddress getProxyAddress() { return this.server.getLocalSocketAddress(); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/0040775 0000000 0000000 00000000000 14315123013 024344 5ustar000000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingHttp1StreamListener.java0100664 0000000 0000000 00000004556 14245617503 032425 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.slf4j.LoggerFactory; import org.slf4j.Logger; public class LoggingHttp1StreamListener implements Http1StreamListener { public static final LoggingHttp1StreamListener INSTANCE = new LoggingHttp1StreamListener(); private final Logger connLog = LoggerFactory.getLogger("org.apache.hc.core5.http.connection"); @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (connLog.isDebugEnabled()) { if (keepAlive) { connLog.debug("{} connection is kept alive", LoggingSupport.getId(connection)); } else { connLog.debug("{} connection is not kept alive", LoggingSupport.getId(connection)); } } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingSupport.java0100664 0000000 0000000 00000003222 14245617503 030204 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import org.apache.hc.core5.util.Identifiable; public final class LoggingSupport { public static String getId(final Object object) { if (object == null) { return null; } if (object instanceof Identifiable) { return ((Identifiable) object).getId(); } else { return object.getClass().getSimpleName() + "-" + Integer.toHexString(System.identityHashCode(object)); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingInputStream.java0100664 0000000 0000000 00000005330 14245617503 031005 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import java.io.InputStream; class LoggingInputStream extends InputStream { private final InputStream in; private final Wire wire; public LoggingInputStream(final InputStream in, final Wire wire) { super(); this.in = in; this.wire = wire; } @Override public int read() throws IOException { final int b = in.read(); if (b != -1) { wire.input(b); } return b; } @Override public int read(final byte[] b) throws IOException { final int bytesRead = in.read(b); if (bytesRead != -1) { wire.input(b, 0, bytesRead); } return bytesRead; } @Override public int read(final byte[] b, final int off, final int len) throws IOException { final int bytesRead = in.read(b, off, len); if (bytesRead != -1) { wire.input(b, off, bytesRead); } return bytesRead; } @Override public long skip(final long n) throws IOException { return super.skip(n); } @Override public int available() throws IOException { return in.available(); } @Override public void mark(final int readlimit) { super.mark(readlimit); } @Override public void reset() throws IOException { super.reset(); } @Override public boolean markSupported() { return false; } @Override public void close() throws IOException { in.close(); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestClient.java0100664 0000000 0000000 00000012360 14245617503 030604 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLContext; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultAddressResolver; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.pool.PoolReusePolicy; import org.apache.hc.core5.pool.StrictConnPool; import org.apache.hc.core5.util.TimeValue; public class ClassicTestClient { private final SSLContext sslContext; private final SocketConfig socketConfig; private final AtomicReference requesterRef; public ClassicTestClient(final SSLContext sslContext, final SocketConfig socketConfig) { super(); this.sslContext = sslContext; this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT; this.requesterRef = new AtomicReference<>(); } public ClassicTestClient(final SocketConfig socketConfig) { this(null, socketConfig); } public ClassicTestClient() { this(null, null); } public void start() { start(null); } public void start(final HttpProcessor httpProcessor) { if (requesterRef.get() == null) { final HttpRequestExecutor requestExecutor = new HttpRequestExecutor( HttpRequestExecutor.DEFAULT_WAIT_FOR_CONTINUE, DefaultConnectionReuseStrategy.INSTANCE, LoggingHttp1StreamListener.INSTANCE); final StrictConnPool connPool = new StrictConnPool<>( 20, 50, TimeValue.NEG_ONE_MILLISECOND, PoolReusePolicy.LIFO, LoggingConnPoolListener.INSTANCE); final HttpRequester requester = new HttpRequester( requestExecutor, httpProcessor != null ? httpProcessor : HttpProcessors.client(), connPool, socketConfig, new LoggingBHttpClientConnectionFactory(Http1Config.DEFAULT, CharCodingConfig.DEFAULT), sslContext != null ? sslContext.getSocketFactory() : null, null, null, DefaultAddressResolver.INSTANCE); requesterRef.compareAndSet(null, requester); } else { throw new IllegalStateException("Requester has already been started"); } } public void shutdown(final CloseMode closeMode) { final HttpRequester requester = requesterRef.getAndSet(null); if (requester != null) { requester.close(closeMode); } } public ClassicHttpResponse execute( final HttpHost targetHost, final ClassicHttpRequest request, final HttpContext context) throws HttpException, IOException { final HttpRequester requester = this.requesterRef.get(); if (requester == null) { throw new IllegalStateException("Requester has not been started"); } if (request.getAuthority() == null) { request.setAuthority(new URIAuthority(targetHost)); } request.setScheme(targetHost.getSchemeName()); return requester.execute(targetHost, request, socketConfig.getSoTimeout(), context); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingOutputStream.java0100664 0000000 0000000 00000004124 14245617503 031206 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import java.io.OutputStream; class LoggingOutputStream extends OutputStream { private final OutputStream out; private final Wire wire; public LoggingOutputStream(final OutputStream out, final Wire wire) { super(); this.out = out; this.wire = wire; } @Override public void write(final int b) throws IOException { wire.output(b); } @Override public void write(final byte[] b) throws IOException { wire.output(b); out.write(b); } @Override public void write(final byte[] b, final int off, final int len) throws IOException { wire.output(b, off, len); out.write(b, off, len); } @Override public void flush() throws IOException { out.flush(); } @Override public void close() throws IOException { out.close(); } } ././@LongLink0100644 0000000 0000000 00000000155 14245617503 011643 Lustar 0000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingBHttpClientConnectionFactory.javahttpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingBHttpClientConnectionFact0100664 0000000 0000000 00000011063 14245617503 032610 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import java.net.Socket; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.CharCodingSupport; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; public class LoggingBHttpClientConnectionFactory implements HttpConnectionFactory { public static final LoggingBHttpClientConnectionFactory INSTANCE = new LoggingBHttpClientConnectionFactory(); private final Http1Config http1Config; private final CharCodingConfig charCodingConfig; private final ContentLengthStrategy incomingContentStrategy; private final ContentLengthStrategy outgoingContentStrategy; private final HttpMessageWriterFactory requestWriterFactory; private final HttpMessageParserFactory responseParserFactory; public LoggingBHttpClientConnectionFactory( final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { super(); this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.incomingContentStrategy = incomingContentStrategy; this.outgoingContentStrategy = outgoingContentStrategy; this.requestWriterFactory = requestWriterFactory; this.responseParserFactory = responseParserFactory; } public LoggingBHttpClientConnectionFactory( final Http1Config http1Config, final CharCodingConfig charCodingConfig, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { this(http1Config, charCodingConfig, null, null, requestWriterFactory, responseParserFactory); } public LoggingBHttpClientConnectionFactory( final Http1Config http1Config, final CharCodingConfig charCodingConfig) { this(http1Config, charCodingConfig, null, null, null, null); } public LoggingBHttpClientConnectionFactory() { this(null, null, null, null, null, null); } @Override public LoggingBHttpClientConnection createConnection(final Socket socket) throws IOException { final LoggingBHttpClientConnection conn = new LoggingBHttpClientConnection( http1Config, CharCodingSupport.createDecoder(charCodingConfig), CharCodingSupport.createEncoder(charCodingConfig), incomingContentStrategy, outgoingContentStrategy, requestWriterFactory, responseParserFactory); conn.bind(socket); return conn; } } ././@LongLink0100644 0000000 0000000 00000000146 14315123013 011625 Lustar 0000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingBHttpClientConnection.javahttpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingBHttpClientConnection.jav0100664 0000000 0000000 00000012014 14315123013 032550 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import java.net.Socket; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnection; import org.apache.hc.core5.http.impl.io.SocketHolder; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Identifiable; import org.slf4j.LoggerFactory; import org.slf4j.Logger; public class LoggingBHttpClientConnection extends DefaultBHttpClientConnection implements Identifiable { private static final AtomicLong COUNT = new AtomicLong(); private final String id; private final Logger log; private final Logger headerLog; private final Wire wire; public LoggingBHttpClientConnection( final Http1Config http1Config, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { super(http1Config, charDecoder, charEncoder, incomingContentStrategy, outgoingContentStrategy, requestWriterFactory, responseParserFactory); this.id = "http-outgoing-" + COUNT.incrementAndGet(); this.log = LoggerFactory.getLogger(getClass()); this.headerLog = LoggerFactory.getLogger("org.apache.hc.core5.http.headers"); this.wire = new Wire(LoggerFactory.getLogger("org.apache.hc.core5.http.wire"), this.id); } public LoggingBHttpClientConnection(final Http1Config http1Config) { this(http1Config, null, null, null, null, null, null); } @Override public String getId() { return id; } @Override public void close() throws IOException { if (this.log.isDebugEnabled()) { this.log.debug("{}: Close connection", this.id); } super.close(); } @Override public void close(final CloseMode closeMode) { if (this.log.isDebugEnabled()) { this.log.debug("{}: Shutdown connection", this.id); } super.close(closeMode); } @Override public void bind(final Socket socket) throws IOException { super.bind(this.wire.isEnabled() ? new LoggingSocketHolder(socket, wire) : new SocketHolder(socket)); } @Override protected void onResponseReceived(final ClassicHttpResponse response) { if (response != null && this.headerLog.isDebugEnabled()) { this.headerLog.debug("{} << {}", this.id, new StatusLine(response)); final Header[] headers = response.getHeaders(); for (final Header header : headers) { this.headerLog.debug("{} << {}", this.id, header); } } } @Override protected void onRequestSubmitted(final ClassicHttpRequest request) { if (request != null && this.headerLog.isDebugEnabled()) { this.headerLog.debug("{} >> {}", id, new RequestLine(request)); final Header[] headers = request.getHeaders(); for (final Header header : headers) { this.headerLog.debug("{} >> {}", this.id, header); } } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingSocketHolder.java0100664 0000000 0000000 00000003647 14245617503 031131 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import org.apache.hc.core5.http.impl.io.SocketHolder; class LoggingSocketHolder extends SocketHolder { private final Wire wire; LoggingSocketHolder(final Socket socket, final Wire wire) { super(socket); this.wire = wire; } @Override protected InputStream getInputStream(final Socket socket) throws IOException { return new LoggingInputStream(super.getInputStream(socket), wire); } @Override protected OutputStream getOutputStream(final Socket socket) throws IOException { return new LoggingOutputStream(super.getOutputStream(socket), wire); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/Wire.java0100664 0000000 0000000 00000007321 14245617503 026133 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.nio.ByteBuffer; import org.apache.hc.core5.http.Chars; import org.slf4j.Logger; public class Wire { private final Logger log; private final String id; public Wire(final Logger log, final String id) { super(); this.log = log; this.id = id; } private void wire(final String header, final byte[] b, final int pos, final int off) { final StringBuilder buffer = new StringBuilder(); for (int i = 0; i < off; i++) { final int ch = b[pos + i]; if (ch == Chars.CR) { buffer.append("[\\r]"); } else if (ch == Chars.LF) { buffer.append("[\\n]\""); buffer.insert(0, "\""); buffer.insert(0, header); this.log.debug("{} {}", this.id, buffer); buffer.setLength(0); } else if ((ch < Chars.SP) || (ch >= Chars.DEL)) { buffer.append("[0x"); buffer.append(Integer.toHexString(ch)); buffer.append("]"); } else { buffer.append((char) ch); } } if (buffer.length() > 0) { buffer.append('\"'); buffer.insert(0, '\"'); buffer.insert(0, header); this.log.debug("{} {}", this.id, buffer); } } public boolean isEnabled() { return this.log.isDebugEnabled(); } public void output(final byte[] b, final int pos, final int off) { wire(">> ", b, pos, off); } public void input(final byte[] b, final int pos, final int off) { wire("<< ", b, pos, off); } public void output(final byte[] b) { output(b, 0, b.length); } public void input(final byte[] b) { input(b, 0, b.length); } public void output(final int b) { output(new byte[] {(byte) b}); } public void input(final int b) { input(new byte[] {(byte) b}); } public void output(final ByteBuffer b) { if (b.hasArray()) { output(b.array(), b.arrayOffset() + b.position(), b.remaining()); } else { final byte[] tmp = new byte[b.remaining()]; b.get(tmp); output(tmp); } } public void input(final ByteBuffer b) { if (b.hasArray()) { input(b.array(), b.arrayOffset() + b.position(), b.remaining()); } else { final byte[] tmp = new byte[b.remaining()]; b.get(tmp); input(tmp); } } } ././@LongLink0100644 0000000 0000000 00000000146 14315123013 011625 Lustar 0000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingBHttpServerConnection.javahttpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingBHttpServerConnection.jav0100664 0000000 0000000 00000012121 14315123013 032577 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import java.net.Socket; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnection; import org.apache.hc.core5.http.impl.io.SocketHolder; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Identifiable; import org.slf4j.LoggerFactory; import org.slf4j.Logger; public class LoggingBHttpServerConnection extends DefaultBHttpServerConnection implements Identifiable { private static final AtomicLong COUNT = new AtomicLong(); private final String id; private final Logger log; private final Logger headerLog; private final Wire wire; public LoggingBHttpServerConnection( final String scheme, final Http1Config http1Config, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final HttpMessageParserFactory requestParserFactory, final HttpMessageWriterFactory responseWriterFactory) { super(scheme, http1Config, charDecoder, charEncoder, incomingContentStrategy, outgoingContentStrategy, requestParserFactory, responseWriterFactory); this.id = "http-incoming-" + COUNT.incrementAndGet(); this.log = LoggerFactory.getLogger(getClass()); this.headerLog = LoggerFactory.getLogger("org.apache.hc.core5.http.headers"); this.wire = new Wire(LoggerFactory.getLogger("org.apache.hc.core5.http.wire"), this.id); } @Override public String getId() { return id; } public LoggingBHttpServerConnection(final String scheme, final Http1Config http1Config) { this(scheme, http1Config, null, null, null, null, null, null); } @Override public void close() throws IOException { if (this.log.isDebugEnabled()) { this.log.debug("{}: Close connection", this.id); } super.close(); } @Override public void close(final CloseMode closeMode) { if (this.log.isDebugEnabled()) { this.log.debug("{}: Shutdown connection", this.id); } super.close(closeMode); } @Override public void bind(final Socket socket) throws IOException { super.bind(this.wire.isEnabled() ? new LoggingSocketHolder(socket, wire) : new SocketHolder(socket)); } @Override protected void onRequestReceived(final ClassicHttpRequest request) { if (request != null && this.headerLog.isDebugEnabled()) { this.headerLog.debug("{} >> {}", id, new RequestLine(request)); final Header[] headers = request.getHeaders(); for (final Header header : headers) { this.headerLog.debug("{} >> {}", this.id, header); } } } @Override protected void onResponseSubmitted(final ClassicHttpResponse response) { if (response != null && this.headerLog.isDebugEnabled()) { this.headerLog.debug("{} << {}", this.id, new StatusLine(response)); final Header[] headers = response.getHeaders(); for (final Header header : headers) { this.headerLog.debug("{} << {}", this.id, header); } } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingExceptionListener.java0100664 0000000 0000000 00000004444 14245617503 032203 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.net.SocketException; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.ExceptionListener; import org.apache.hc.core5.http.HttpConnection; import org.slf4j.LoggerFactory; import org.slf4j.Logger; public class LoggingExceptionListener implements ExceptionListener { public final static LoggingExceptionListener INSTANCE = new LoggingExceptionListener(); private final Logger connLog = LoggerFactory.getLogger("org.apache.hc.core5.http.connection"); @Override public void onError(final Exception ex) { if (ex instanceof SocketException) { connLog.debug(ex.getMessage()); } else { connLog.error(ex.getMessage(), ex); } } @Override public void onError(final HttpConnection conn, final Exception ex) { if (ex instanceof ConnectionClosedException) { connLog.debug(ex.getMessage()); } else if (ex instanceof SocketException) { connLog.debug(ex.getMessage()); } else { connLog.error(ex.getMessage(), ex); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingConnPoolListener.java0100664 0000000 0000000 00000005773 14245617503 032002 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.pool.ConnPoolListener; import org.apache.hc.core5.pool.ConnPoolStats; import org.apache.hc.core5.pool.PoolStats; import org.slf4j.LoggerFactory; import org.slf4j.Logger; public class LoggingConnPoolListener implements ConnPoolListener { public final static LoggingConnPoolListener INSTANCE = new LoggingConnPoolListener(); private final Logger connLog = LoggerFactory.getLogger("org.apache.hc.core5.http.connection"); private LoggingConnPoolListener() { } @Override public void onLease(final HttpHost route, final ConnPoolStats connPoolStats) { if (connLog.isDebugEnabled()) { final StringBuilder buf = new StringBuilder(); buf.append("Leased ").append(route).append(" "); final PoolStats totals = connPoolStats.getTotalStats(); buf.append(" total kept alive: ").append(totals.getAvailable()).append("; "); buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable()); buf.append(" of ").append(totals.getMax()); connLog.debug(buf.toString()); } } @Override public void onRelease(final HttpHost route, final ConnPoolStats connPoolStats) { if (connLog.isDebugEnabled()) { final StringBuilder buf = new StringBuilder(); buf.append("Released ").append(route).append(" "); final PoolStats totals = connPoolStats.getTotalStats(); buf.append(" total kept alive: ").append(totals.getAvailable()).append("; "); buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable()); buf.append(" of ").append(totals.getMax()); connLog.debug(buf.toString()); } } } ././@LongLink0100644 0000000 0000000 00000000155 14245617503 011643 Lustar 0000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingBHttpServerConnectionFactory.javahttpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/LoggingBHttpServerConnectionFact0100664 0000000 0000000 00000011651 14245617503 032643 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import java.net.Socket; import javax.net.ssl.SSLSocket; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.CharCodingSupport; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; public class LoggingBHttpServerConnectionFactory implements HttpConnectionFactory { public static final LoggingBHttpServerConnectionFactory INSTANCE = new LoggingBHttpServerConnectionFactory(); private final String scheme; private final Http1Config http1Config; private final CharCodingConfig charCodingConfig; private final ContentLengthStrategy incomingContentStrategy; private final ContentLengthStrategy outgoingContentStrategy; private final HttpMessageParserFactory requestParserFactory; private final HttpMessageWriterFactory responseWriterFactory; public LoggingBHttpServerConnectionFactory( final String scheme, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final HttpMessageParserFactory requestParserFactory, final HttpMessageWriterFactory responseWriterFactory) { super(); this.scheme = scheme; this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.incomingContentStrategy = incomingContentStrategy; this.outgoingContentStrategy = outgoingContentStrategy; this.requestParserFactory = requestParserFactory; this.responseWriterFactory = responseWriterFactory; } public LoggingBHttpServerConnectionFactory( final String scheme, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final HttpMessageParserFactory requestParserFactory, final HttpMessageWriterFactory responseWriterFactory) { this(scheme, http1Config, charCodingConfig, null, null, requestParserFactory, responseWriterFactory); } public LoggingBHttpServerConnectionFactory( final String scheme, final Http1Config http1Config, final CharCodingConfig charCodingConfig) { this(scheme, http1Config, charCodingConfig, null, null, null, null); } public LoggingBHttpServerConnectionFactory() { this(null, null, null, null, null, null, null); } @Override public LoggingBHttpServerConnection createConnection(final Socket socket) throws IOException { final LoggingBHttpServerConnection conn = new LoggingBHttpServerConnection( scheme != null ? scheme : (socket instanceof SSLSocket ? URIScheme.HTTPS.id : URIScheme.HTTP.id), http1Config, CharCodingSupport.createDecoder(charCodingConfig), CharCodingSupport.createEncoder(charCodingConfig), incomingContentStrategy, outgoingContentStrategy, requestParserFactory, responseWriterFactory); conn.bind(socket); return conn; } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java0100664 0000000 0000000 00000013270 14245617503 030635 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import java.net.InetAddress; import java.util.concurrent.atomic.AtomicReference; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLContext; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.io.HttpService; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.HttpServerRequestHandler; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.support.BasicHttpServerExpectationDecorator; import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestHandlerRegistry; import org.apache.hc.core5.io.CloseMode; public class ClassicTestServer { private final SSLContext sslContext; private final SocketConfig socketConfig; private final RequestHandlerRegistry registry; private final AtomicReference serverRef; public ClassicTestServer(final SSLContext sslContext, final SocketConfig socketConfig) { super(); this.sslContext = sslContext; this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT; this.registry = new RequestHandlerRegistry<>(); this.serverRef = new AtomicReference<>(); } public ClassicTestServer(final SocketConfig socketConfig) { this(null, socketConfig); } public ClassicTestServer() { this(null, null); } public void registerHandler(final String pattern, final HttpRequestHandler handler) { this.registry.register(null, pattern, handler); } public void registerHandlerVirtual(final String hostname, final String pattern, final HttpRequestHandler handler) { this.registry.register(hostname, pattern, handler); } public int getPort() { final HttpServer server = this.serverRef.get(); if (server != null) { return server.getLocalPort(); } throw new IllegalStateException("Server not running"); } public InetAddress getInetAddress() { final HttpServer server = this.serverRef.get(); if (server != null) { return server.getInetAddress(); } throw new IllegalStateException("Server not running"); } public void start( final Http1Config http1Config, final HttpProcessor httpProcessor, final Decorator handlerDecorator) throws IOException { if (serverRef.get() == null) { final HttpServerRequestHandler handler = new BasicHttpServerRequestHandler(registry); final HttpService httpService = new HttpService( httpProcessor != null ? httpProcessor : HttpProcessors.server(), handlerDecorator != null ? handlerDecorator.decorate(handler) : new BasicHttpServerExpectationDecorator(handler), DefaultConnectionReuseStrategy.INSTANCE, LoggingHttp1StreamListener.INSTANCE); final HttpServer server = new HttpServer( 0, httpService, null, socketConfig, sslContext != null ? sslContext.getServerSocketFactory() : ServerSocketFactory.getDefault(), new LoggingBHttpServerConnectionFactory( sslContext != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id, http1Config != null ? http1Config : Http1Config.DEFAULT, CharCodingConfig.DEFAULT), null, LoggingExceptionListener.INSTANCE); if (serverRef.compareAndSet(null, server)) { server.start(); } } else { throw new IllegalStateException("Server already running"); } } public void start() throws IOException { start(null, null, null); } public void shutdown(final CloseMode closeMode) { final HttpServer server = serverRef.getAndSet(null); if (server != null) { server.close(closeMode); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/0040775 0000000 0000000 00000000000 14437151052 023521 5ustar000000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/H2TestServer.java0100664 0000000 0000000 00000014474 14403631147 026674 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.Future; import javax.net.ssl.SSLContext; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator; import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler; import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestHandlerRegistry; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.impl.H2Processors; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; public class H2TestServer extends AsyncServer { private final SSLContext sslContext; private final SSLSessionInitializer sslSessionInitializer; private final SSLSessionVerifier sslSessionVerifier; private final RequestHandlerRegistry> registry; public H2TestServer( final IOReactorConfig ioReactorConfig, final SSLContext sslContext, final SSLSessionInitializer sslSessionInitializer, final SSLSessionVerifier sslSessionVerifier) throws IOException { super(ioReactorConfig); this.sslContext = sslContext; this.sslSessionInitializer = sslSessionInitializer; this.sslSessionVerifier = sslSessionVerifier; this.registry = new RequestHandlerRegistry<>(); } public H2TestServer() throws IOException { this(IOReactorConfig.DEFAULT, null, null, null); } public void register(final String uriPattern, final Supplier supplier) { registry.register(null, uriPattern, supplier); } public void register( final String uriPattern, final AsyncServerRequestHandler requestHandler) { register(uriPattern, () -> new BasicServerExchangeHandler<>(requestHandler)); } public void start(final IOEventHandlerFactory handlerFactory) throws IOException { execute(handlerFactory); } public InetSocketAddress start( final HttpProcessor httpProcessor, final Decorator exchangeHandlerDecorator, final H2Config h2Config) throws Exception { start(new InternalServerProtocolNegotiationStarter( httpProcessor != null ? httpProcessor : H2Processors.server(), new DefaultAsyncResponseExchangeHandlerFactory( registry, exchangeHandlerDecorator != null ? exchangeHandlerDecorator : BasicAsyncServerExpectationDecorator::new), HttpVersionPolicy.FORCE_HTTP_2, h2Config, Http1Config.DEFAULT, CharCodingConfig.DEFAULT, sslContext, sslSessionInitializer, sslSessionVerifier)); final Future future = listen(new InetSocketAddress(0)); final ListenerEndpoint listener = future.get(); return (InetSocketAddress) listener.getAddress(); } public InetSocketAddress start( final HttpProcessor httpProcessor, final Decorator exchangeHandlerDecorator, final Http1Config http1Config) throws Exception { start(new InternalServerProtocolNegotiationStarter( httpProcessor != null ? httpProcessor : HttpProcessors.server(), new DefaultAsyncResponseExchangeHandlerFactory( registry, exchangeHandlerDecorator != null ? exchangeHandlerDecorator : BasicAsyncServerExpectationDecorator::new), HttpVersionPolicy.FORCE_HTTP_1, H2Config.DEFAULT, http1Config, CharCodingConfig.DEFAULT, sslContext, sslSessionInitializer, sslSessionVerifier)); final Future future = listen(new InetSocketAddress(0)); final ListenerEndpoint listener = future.get(); return (InetSocketAddress) listener.getAddress(); } public InetSocketAddress start(final H2Config h2Config) throws Exception { return start(null, null, h2Config); } public InetSocketAddress start(final Http1Config http1Config) throws Exception { return start(null, null, http1Config); } public InetSocketAddress start() throws Exception { return start(H2Config.DEFAULT); } } ././@LongLink0100644 0000000 0000000 00000000154 14245617503 011642 Lustar 0000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp1EventHandlerFactory.javahttpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp1EventHandlerFacto0100664 0000000 0000000 00000015652 14245617503 032620 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import javax.net.ssl.SSLContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.nio.DefaultHttpRequestParserFactory; import org.apache.hc.core5.http.impl.nio.DefaultHttpResponseWriterFactory; import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandler; import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.NHttpMessageParser; import org.apache.hc.core5.http.nio.NHttpMessageParserFactory; import org.apache.hc.core5.http.nio.NHttpMessageWriter; import org.apache.hc.core5.http.nio.NHttpMessageWriterFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.util.Args; /** * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) class InternalServerHttp1EventHandlerFactory implements IOEventHandlerFactory { private final HttpProcessor httpProcessor; private final HandlerFactory exchangeHandlerFactory; private final Http1Config http1Config; private final CharCodingConfig charCodingConfig; private final ConnectionReuseStrategy connectionReuseStrategy; private final SSLContext sslContext; private final SSLSessionInitializer sslSessionInitializer; private final SSLSessionVerifier sslSessionVerifier; private final NHttpMessageParserFactory requestParserFactory; private final NHttpMessageWriterFactory responseWriterFactory; InternalServerHttp1EventHandlerFactory( final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ConnectionReuseStrategy connectionReuseStrategy, final SSLContext sslContext, final SSLSessionInitializer sslSessionInitializer, final SSLSessionVerifier sslSessionVerifier) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.exchangeHandlerFactory = Args.notNull(exchangeHandlerFactory, "Exchange handler factory"); this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.connectionReuseStrategy = connectionReuseStrategy != null ? connectionReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE; this.sslContext = sslContext; this.sslSessionInitializer = sslSessionInitializer; this.sslSessionVerifier = sslSessionVerifier; this.requestParserFactory = new DefaultHttpRequestParserFactory(this.http1Config); this.responseWriterFactory = DefaultHttpResponseWriterFactory.INSTANCE; } protected ServerHttp1StreamDuplexer createServerHttp1StreamDuplexer( final ProtocolIOSession ioSession, final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ConnectionReuseStrategy connectionReuseStrategy, final NHttpMessageParser incomingMessageParser, final NHttpMessageWriter outgoingMessageWriter, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final Http1StreamListener streamListener) { return new ServerHttp1StreamDuplexer(ioSession, httpProcessor, exchangeHandlerFactory, sslContext != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id, http1Config, charCodingConfig, connectionReuseStrategy, incomingMessageParser, outgoingMessageWriter, incomingContentStrategy, outgoingContentStrategy, streamListener); } @Override public IOEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { if (sslContext != null) { ioSession.startTls(sslContext, null, null ,sslSessionInitializer, sslSessionVerifier, null); } final ServerHttp1StreamDuplexer streamDuplexer = createServerHttp1StreamDuplexer( ioSession, httpProcessor, exchangeHandlerFactory, http1Config, charCodingConfig, connectionReuseStrategy, requestParserFactory.create(), responseWriterFactory.create(), DefaultContentLengthStrategy.INSTANCE, DefaultContentLengthStrategy.INSTANCE, LoggingHttp1StreamListener.INSTANCE_SERVER); return new ServerHttp1IOEventHandler(streamDuplexer); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/H2TestClient.java0100664 0000000 0000000 00000013624 14323605260 026636 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.util.concurrent.Future; import javax.net.ssl.SSLContext; import org.apache.hc.core5.concurrent.FutureContribution; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestHandlerRegistry; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.impl.H2Processors; import org.apache.hc.core5.http2.nio.support.DefaultAsyncPushConsumerFactory; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; public class H2TestClient extends AsyncRequester { private final SSLContext sslContext; private final SSLSessionInitializer sslSessionInitializer; private final SSLSessionVerifier sslSessionVerifier; private final RequestHandlerRegistry> registry; public H2TestClient( final IOReactorConfig ioReactorConfig, final SSLContext sslContext, final SSLSessionInitializer sslSessionInitializer, final SSLSessionVerifier sslSessionVerifier) throws IOException { super(ioReactorConfig); this.sslContext = sslContext; this.sslSessionInitializer = sslSessionInitializer; this.sslSessionVerifier = sslSessionVerifier; this.registry = new RequestHandlerRegistry<>(); } public H2TestClient() throws IOException { this(IOReactorConfig.DEFAULT, null, null, null); } public void register(final String uriPattern, final Supplier supplier) { Args.notNull(uriPattern, "URI pattern"); Args.notNull(supplier, "Supplier"); registry.register(null, uriPattern, supplier); } public void start(final IOEventHandlerFactory handlerFactory) throws IOException { super.execute(handlerFactory); } public void start(final HttpProcessor httpProcessor, final H2Config h2Config) throws IOException { start(new InternalClientProtocolNegotiationStarter( httpProcessor, new DefaultAsyncPushConsumerFactory(registry), HttpVersionPolicy.FORCE_HTTP_2, h2Config, Http1Config.DEFAULT, CharCodingConfig.DEFAULT, sslContext, sslSessionInitializer, sslSessionVerifier)); } public void start(final HttpProcessor httpProcessor, final Http1Config http1Config) throws IOException { start(new InternalClientProtocolNegotiationStarter( httpProcessor, new DefaultAsyncPushConsumerFactory(registry), HttpVersionPolicy.FORCE_HTTP_1, H2Config.DEFAULT, http1Config, CharCodingConfig.DEFAULT, sslContext, sslSessionInitializer, sslSessionVerifier)); } public void start(final H2Config h2Config) throws IOException { start(H2Processors.client(), h2Config); } public void start(final Http1Config http1Config) throws IOException { start(H2Processors.client(), http1Config); } public void start() throws Exception { start(H2Config.DEFAULT); } public Future connect( final HttpHost host, final Timeout timeout, final FutureCallback callback) { final BasicFuture future = new BasicFuture<>(callback); requestSession(host, timeout, new FutureContribution(future) { @Override public void completed(final IOSession session) { future.completed(new ClientSessionEndpoint(session)); } }); return future; } public Future connect(final HttpHost host,final Timeout timeout) { return connect(host, timeout, null); } public Future connect(final String hostname, final int port, final Timeout timeout) { return connect(new HttpHost(hostname, port), timeout, null); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestClient.java0100664 0000000 0000000 00000010546 14245617503 027373 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.util.concurrent.Future; import javax.net.ssl.SSLContext; import org.apache.hc.core5.concurrent.FutureContribution; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.util.Timeout; public class Http1TestClient extends AsyncRequester { private final SSLContext sslContext; private final SSLSessionInitializer sslSessionInitializer; private final SSLSessionVerifier sslSessionVerifier; public Http1TestClient( final IOReactorConfig ioReactorConfig, final SSLContext sslContext, final SSLSessionInitializer sslSessionInitializer, final SSLSessionVerifier sslSessionVerifier) throws IOException { super(ioReactorConfig); this.sslContext = sslContext; this.sslSessionInitializer = sslSessionInitializer; this.sslSessionVerifier = sslSessionVerifier; } public Http1TestClient() throws IOException { this(IOReactorConfig.DEFAULT, null, null, null); } public void start( final HttpProcessor httpProcessor, final Http1Config http1Config) throws IOException { execute(new InternalClientHttp1EventHandlerFactory( httpProcessor, http1Config, CharCodingConfig.DEFAULT, DefaultConnectionReuseStrategy.INSTANCE, sslContext, sslSessionInitializer, sslSessionVerifier)); } public void start(final Http1Config http1Config) throws IOException { start(HttpProcessors.client(), http1Config); } public void start() throws IOException { start(Http1Config.DEFAULT); } public Future connect( final HttpHost host, final Timeout timeout, final FutureCallback callback) { final BasicFuture future = new BasicFuture<>(callback); requestSession(host, timeout, new FutureContribution(future) { @Override public void completed(final IOSession session) { future.completed(new ClientSessionEndpoint(session)); } }); return future; } public Future connect(final HttpHost host, final Timeout timeout) { return connect(host, timeout, null); } public Future connect(final String hostname, final int port, final Timeout timeout) { return connect(new HttpHost(hostname, port), timeout, null); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LoggingHttp1StreamListener.java0100664 0000000 0000000 00000007632 14315123013 031551 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.util.Iterator; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.testing.classic.LoggingSupport; import org.slf4j.LoggerFactory; import org.slf4j.Logger; public class LoggingHttp1StreamListener implements Http1StreamListener { enum Type { CLIENT, SERVER } public final static LoggingHttp1StreamListener INSTANCE_CLIENT = new LoggingHttp1StreamListener(Type.CLIENT); public final static LoggingHttp1StreamListener INSTANCE_SERVER = new LoggingHttp1StreamListener(Type.SERVER); private final Logger connLog = LoggerFactory.getLogger("org.apache.hc.core5.http.connection"); private final Logger headerLog = LoggerFactory.getLogger("org.apache.hc.core5.http.headers"); private final String requestDirection; private final String responseDirection; private LoggingHttp1StreamListener(final Type type) { this.requestDirection = type == Type.CLIENT ? " >> " : " << "; this.responseDirection = type == Type.CLIENT ? " << " : " >> "; } @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { if (headerLog.isDebugEnabled()) { final String idRequestDirection = LoggingSupport.getId(connection) + requestDirection; headerLog.debug("{}{}", idRequestDirection, new RequestLine(request)); for (final Iterator
it = request.headerIterator(); it.hasNext(); ) { headerLog.debug("{}{}", idRequestDirection, it.next()); } } } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { if (headerLog.isDebugEnabled()) { final String id = LoggingSupport.getId(connection); headerLog.debug("{}{}{}", id, responseDirection, new StatusLine(response)); for (final Iterator
it = response.headerIterator(); it.hasNext(); ) { headerLog.debug("{}{}{}", id, responseDirection, it.next()); } } } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (connLog.isDebugEnabled()) { if (keepAlive) { connLog.debug("{} connection is kept alive", LoggingSupport.getId(connection)); } else { connLog.debug("{} connection is not kept alive", LoggingSupport.getId(connection)); } } } }././@LongLink0100644 0000000 0000000 00000000156 14403631147 011640 Lustar 0000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerProtocolNegotiationStarter.javahttpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerProtocolNegotiationSta0100664 0000000 0000000 00000014026 14403631147 033003 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import javax.net.ssl.SSLContext; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandler; import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.impl.H2Processors; import org.apache.hc.core5.http2.impl.nio.HttpProtocolNegotiator; import org.apache.hc.core5.http2.impl.nio.ServerH2PrefaceHandler; import org.apache.hc.core5.http2.impl.nio.ServerH2StreamMultiplexerFactory; import org.apache.hc.core5.http2.impl.nio.ServerH2UpgradeHandler; import org.apache.hc.core5.http2.impl.nio.ServerHttp1UpgradeHandler; import org.apache.hc.core5.http2.ssl.ApplicationProtocol; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.util.Args; class InternalServerProtocolNegotiationStarter implements IOEventHandlerFactory { private final HttpProcessor httpProcessor; private final HandlerFactory exchangeHandlerFactory; private final HttpVersionPolicy versionPolicy; private final H2Config h2Config; private final Http1Config http1Config; private final CharCodingConfig charCodingConfig; private final SSLContext sslContext; private final SSLSessionInitializer sslSessionInitializer; private final SSLSessionVerifier sslSessionVerifier; public InternalServerProtocolNegotiationStarter( final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final HttpVersionPolicy versionPolicy, final H2Config h2Config, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final SSLContext sslContext, final SSLSessionInitializer sslSessionInitializer, final SSLSessionVerifier sslSessionVerifier) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.exchangeHandlerFactory = Args.notNull(exchangeHandlerFactory, "Exchange handler factory"); this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE; this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT; this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.sslContext = sslContext; this.sslSessionInitializer = sslSessionInitializer; this.sslSessionVerifier = sslSessionVerifier; } @Override public IOEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { if (sslContext != null) { ioSession.startTls(sslContext, null, null, sslSessionInitializer, sslSessionVerifier, null); } final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ServerHttp1StreamDuplexerFactory( httpProcessor != null ? httpProcessor : HttpProcessors.server(), exchangeHandlerFactory, http1Config, charCodingConfig, LoggingHttp1StreamListener.INSTANCE_SERVER); final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory = new ServerH2StreamMultiplexerFactory( httpProcessor != null ? httpProcessor : H2Processors.server(), exchangeHandlerFactory, h2Config, charCodingConfig, LoggingH2StreamListener.INSTANCE); ioSession.registerProtocol(ApplicationProtocol.HTTP_1_1.id, new ServerHttp1UpgradeHandler(http1StreamHandlerFactory)); ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ServerH2UpgradeHandler(http2StreamHandlerFactory)); switch (versionPolicy) { case FORCE_HTTP_2: return new ServerH2PrefaceHandler(ioSession, http2StreamHandlerFactory); case FORCE_HTTP_1: return new ServerHttp1IOEventHandler(http1StreamHandlerFactory.create( sslContext != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id, ioSession)); default: return new HttpProtocolNegotiator(ioSession, null); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LoggingIOSessionDecorator.java0100664 0000000 0000000 00000003673 14245617503 031424 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.reactor.IOSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Internal public class LoggingIOSessionDecorator implements Decorator { public final static LoggingIOSessionDecorator INSTANCE = new LoggingIOSessionDecorator(); private final Logger wireLog = LoggerFactory.getLogger("org.apache.hc.core5.http.wire"); private LoggingIOSessionDecorator() { } @Override public IOSession decorate(final IOSession ioSession) { final Logger sessionLog = LoggerFactory.getLogger(ioSession.getClass()); return new LoggingIOSession(ioSession, sessionLog, wireLog); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestServer.java0100664 0000000 0000000 00000012207 14403631147 027413 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.Future; import javax.net.ssl.SSLContext; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator; import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler; import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestHandlerRegistry; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; public class Http1TestServer extends AsyncServer { private final RequestHandlerRegistry> registry; private final SSLContext sslContext; private final SSLSessionInitializer sslSessionInitializer; private final SSLSessionVerifier sslSessionVerifier; public Http1TestServer( final IOReactorConfig ioReactorConfig, final SSLContext sslContext, final SSLSessionInitializer sslSessionInitializer, final SSLSessionVerifier sslSessionVerifier) throws IOException { super(ioReactorConfig); this.registry = new RequestHandlerRegistry<>(); this.sslContext = sslContext; this.sslSessionInitializer = sslSessionInitializer; this.sslSessionVerifier = sslSessionVerifier; } public Http1TestServer() throws IOException { this(IOReactorConfig.DEFAULT, null, null, null); } public void register(final String uriPattern, final Supplier supplier) { registry.register(null, uriPattern, supplier); } public void register( final String uriPattern, final AsyncServerRequestHandler requestHandler) { register(uriPattern, () -> new BasicServerExchangeHandler<>(requestHandler)); } public InetSocketAddress start(final IOEventHandlerFactory handlerFactory) throws Exception { execute(handlerFactory); final Future future = listen(new InetSocketAddress(0)); final ListenerEndpoint listener = future.get(); return (InetSocketAddress) listener.getAddress(); } public InetSocketAddress start( final HttpProcessor httpProcessor, final Decorator exchangeHandlerDecorator, final Http1Config http1Config) throws Exception { return start(new InternalServerHttp1EventHandlerFactory( httpProcessor != null ? httpProcessor : HttpProcessors.server(), new DefaultAsyncResponseExchangeHandlerFactory( registry, exchangeHandlerDecorator != null ? exchangeHandlerDecorator : BasicAsyncServerExpectationDecorator::new), http1Config, CharCodingConfig.DEFAULT, DefaultConnectionReuseStrategy.INSTANCE, sslContext, sslSessionInitializer, sslSessionVerifier)); } public InetSocketAddress start(final HttpProcessor httpProcessor, final Http1Config http1Config) throws Exception { return start(httpProcessor, null, http1Config); } public InetSocketAddress start() throws Exception { return start(null, null, Http1Config.DEFAULT); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LoggingIOSessionListener.java0100664 0000000 0000000 00000006066 14437151052 031261 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoggingIOSessionListener implements IOSessionListener { public final static LoggingIOSessionListener INSTANCE = new LoggingIOSessionListener(); private final Logger connLog = LoggerFactory.getLogger("org.apache.hc.core5.http.connection"); @Internal public LoggingIOSessionListener() { } @Override public void connected(final IOSession session) { if (connLog.isDebugEnabled()) { connLog.debug("{} connected", session); } } @Override public void startTls(final IOSession session) { if (connLog.isDebugEnabled()) { connLog.debug("{} TLS started", session); } } @Override public void inputReady(final IOSession session) { if (connLog.isDebugEnabled()) { connLog.debug("{} input ready", session); } } @Override public void outputReady(final IOSession session) { if (connLog.isDebugEnabled()) { connLog.debug("{} output ready", session); } } @Override public void timeout(final IOSession session) { if (connLog.isDebugEnabled()) { connLog.debug("{} timeout", session); } } @Override public void exception(final IOSession session, final Exception ex) { if (ex instanceof ConnectionClosedException) { return; } connLog.error("{} {}", session, ex.getMessage(), ex); } @Override public void disconnected(final IOSession session) { if (connLog.isDebugEnabled()) { connLog.debug("{} disconnected", session); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/AsyncServer.java0100664 0000000 0000000 00000005406 14245617503 026637 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.reactor.DefaultListeningIOReactor; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ListenerEndpoint; public class AsyncServer extends IOReactorExecutor { public AsyncServer(final IOReactorConfig ioReactorConfig) { super(ioReactorConfig, null); } @Override DefaultListeningIOReactor createIOReactor( final IOEventHandlerFactory ioEventHandlerFactory, final IOReactorConfig ioReactorConfig, final ThreadFactory threadFactory, final Callback sessionShutdownCallback) throws IOException { return new DefaultListeningIOReactor( ioEventHandlerFactory, ioReactorConfig, threadFactory, threadFactory, LoggingIOSessionDecorator.INSTANCE, LoggingExceptionCallback.INSTANCE, LoggingIOSessionListener.INSTANCE, sessionShutdownCallback); } public Future listen(final InetSocketAddress address) { return reactor().listen(address, null); } public Set getEndpoints() { return reactor().getEndpoints(); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/ClientSessionEndpoint.java0100664 0000000 0000000 00000013700 14245617503 030652 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.concurrent.FutureContribution; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.command.RequestExecutionCommand; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.http.nio.support.BasicClientExchangeHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Asserts; /** * Client endpoint that can be used to initiate HTTP message exchanges. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public final class ClientSessionEndpoint implements ModalCloseable { private final IOSession ioSession; private final AtomicBoolean closed; public ClientSessionEndpoint(final IOSession ioSession) { super(); this.ioSession = ioSession; this.closed = new AtomicBoolean(false); } public void execute(final Command command, final Command.Priority priority) { ioSession.enqueue(command, priority); if (!ioSession.isOpen()) { command.cancel(); } } public void execute( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final HttpContext context) { Asserts.check(!closed.get(), "Connection is already closed"); final Command executionCommand = new RequestExecutionCommand(exchangeHandler, pushHandlerFactory, null, context); ioSession.enqueue(executionCommand, Command.Priority.NORMAL); if (!ioSession.isOpen()) { exchangeHandler.failed(new ConnectionClosedException()); } } public void execute( final AsyncClientExchangeHandler exchangeHandler, final HttpContext context) { execute(exchangeHandler, null, context); } public Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HandlerFactory pushHandlerFactory, final HttpContext context, final FutureCallback callback) { Asserts.check(!closed.get(), "Connection is already closed"); final BasicFuture future = new BasicFuture<>(callback); execute(new BasicClientExchangeHandler<>(requestProducer, responseConsumer, new FutureContribution(future) { @Override public void completed(final T result) { future.completed(result); } }), pushHandlerFactory, context); return future; } public Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HttpContext context, final FutureCallback callback) { return execute(requestProducer, responseConsumer, null, context, callback); } public Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final FutureCallback callback) { return execute(requestProducer, responseConsumer, null, null, callback); } public boolean isOpen() { return !closed.get() && ioSession.isOpen(); } @Override public void close(final CloseMode closeMode) { if (closed.compareAndSet(false, true)) { if (closeMode == CloseMode.GRACEFUL) { ioSession.enqueue(ShutdownCommand.GRACEFUL, Command.Priority.NORMAL); } else { ioSession.close(closeMode); } } } @Override public void close() throws IOException { if (closed.compareAndSet(false, true)) { ioSession.enqueue(ShutdownCommand.GRACEFUL, Command.Priority.IMMEDIATE); } } @Override public String toString() { return ioSession.toString(); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/IOReactorExecutor.java0100664 0000000 0000000 00000012067 14245617503 027742 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOReactorService; import org.apache.hc.core5.reactor.IOReactorStatus; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.TimeValue; abstract class IOReactorExecutor implements AutoCloseable { enum Status { READY, RUNNING, TERMINATED } private final IOReactorConfig ioReactorConfig; private final ThreadFactory workerThreadFactory; private final AtomicReference ioReactorRef; private final AtomicReference status; IOReactorExecutor(final IOReactorConfig ioReactorConfig, final ThreadFactory workerThreadFactory) { super(); this.ioReactorConfig = ioReactorConfig != null ? ioReactorConfig : IOReactorConfig.DEFAULT; this.workerThreadFactory = workerThreadFactory; this.ioReactorRef = new AtomicReference<>(); this.status = new AtomicReference<>(Status.READY); } abstract T createIOReactor( IOEventHandlerFactory ioEventHandlerFactory, IOReactorConfig ioReactorConfig, ThreadFactory threadFactory, Callback sessionShutdownCallback) throws IOException; protected void execute(final IOEventHandlerFactory ioEventHandlerFactory) throws IOException { Args.notNull(ioEventHandlerFactory, "Handler factory"); if (ioReactorRef.compareAndSet(null, createIOReactor( ioEventHandlerFactory, ioReactorConfig, workerThreadFactory, ShutdownCommand.GRACEFUL_NORMAL_CALLBACK))) { if (status.compareAndSet(Status.READY, Status.RUNNING)) { ioReactorRef.get().start(); } } else { throw new IllegalStateException("I/O reactor has already been started"); } } private T ensureRunning() { final T ioReactor = ioReactorRef.get(); Asserts.check(ioReactor != null, "I/O reactor has not been started"); return ioReactor; } T reactor() { return ensureRunning(); } public IOReactorStatus getStatus() { final T ioReactor = ioReactorRef.get(); return ioReactor != null ? ioReactor.getStatus() : IOReactorStatus.INACTIVE; } public void awaitShutdown(final TimeValue waitTime) throws InterruptedException { Args.notNull(waitTime, "Wait time"); final T ioReactor = ioReactorRef.get(); if (ioReactor != null) { ioReactor.awaitShutdown(waitTime); } } public void initiateShutdown() { final T ioReactor = ioReactorRef.get(); if (ioReactor != null) { if (status.compareAndSet(Status.RUNNING, Status.TERMINATED)) { ioReactor.initiateShutdown(); } } } public void shutdown(final TimeValue graceTime) { Args.notNull(graceTime, "Grace time"); final T ioReactor = ioReactorRef.get(); if (ioReactor != null) { if (status.compareAndSet(Status.RUNNING, Status.TERMINATED)) { ioReactor.initiateShutdown(); } try { ioReactor.awaitShutdown(graceTime); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); } ioReactor.close(CloseMode.IMMEDIATE); } } @Override public void close() throws Exception { shutdown(TimeValue.ofSeconds(5)); } } ././@LongLink0100644 0000000 0000000 00000000154 14245617503 011642 Lustar 0000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp1EventHandlerFactory.javahttpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp1EventHandlerFacto0100664 0000000 0000000 00000014427 14245617503 032567 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import javax.net.ssl.SSLContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler; import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexer; import org.apache.hc.core5.http.impl.nio.DefaultHttpRequestWriterFactory; import org.apache.hc.core5.http.impl.nio.DefaultHttpResponseParserFactory; import org.apache.hc.core5.http.nio.NHttpMessageParser; import org.apache.hc.core5.http.nio.NHttpMessageParserFactory; import org.apache.hc.core5.http.nio.NHttpMessageWriter; import org.apache.hc.core5.http.nio.NHttpMessageWriterFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.util.Args; /** * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) class InternalClientHttp1EventHandlerFactory implements IOEventHandlerFactory { private final HttpProcessor httpProcessor; private final Http1Config http1Config; private final CharCodingConfig charCodingConfig; private final ConnectionReuseStrategy connectionReuseStrategy; private final SSLContext sslContext; private final SSLSessionInitializer sslSessionInitializer; private final SSLSessionVerifier sslSessionVerifier; private final NHttpMessageParserFactory responseParserFactory; private final NHttpMessageWriterFactory requestWriterFactory; InternalClientHttp1EventHandlerFactory( final HttpProcessor httpProcessor, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ConnectionReuseStrategy connectionReuseStrategy, final SSLContext sslContext, final SSLSessionInitializer sslSessionInitializer, final SSLSessionVerifier sslSessionVerifier) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.connectionReuseStrategy = connectionReuseStrategy != null ? connectionReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE; this.sslContext = sslContext; this.responseParserFactory = new DefaultHttpResponseParserFactory(this.http1Config); this.requestWriterFactory = DefaultHttpRequestWriterFactory.INSTANCE; this.sslSessionInitializer = sslSessionInitializer; this.sslSessionVerifier = sslSessionVerifier; } protected ClientHttp1StreamDuplexer createClientHttp1StreamDuplexer( final ProtocolIOSession ioSession, final HttpProcessor httpProcessor, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final ConnectionReuseStrategy connectionReuseStrategy, final NHttpMessageParser incomingMessageParser, final NHttpMessageWriter outgoingMessageWriter, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final Http1StreamListener streamListener) { return new ClientHttp1StreamDuplexer(ioSession, httpProcessor, http1Config, charCodingConfig, connectionReuseStrategy, incomingMessageParser, outgoingMessageWriter, incomingContentStrategy, outgoingContentStrategy, streamListener); } @Override public IOEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { if (sslContext != null) { ioSession.startTls(sslContext, null, null, sslSessionInitializer, sslSessionVerifier, null); } final ClientHttp1StreamDuplexer streamDuplexer = createClientHttp1StreamDuplexer( ioSession, httpProcessor, http1Config, charCodingConfig, connectionReuseStrategy, responseParserFactory.create(), requestWriterFactory.create(), DefaultContentLengthStrategy.INSTANCE, DefaultContentLengthStrategy.INSTANCE, LoggingHttp1StreamListener.INSTANCE_CLIENT); return new ClientHttp1IOEventHandler(streamDuplexer); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LogAppendable.java0100664 0000000 0000000 00000004572 14245617503 027073 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import org.slf4j.Logger; class LogAppendable implements Appendable { private final Logger log; private final String prefix; private final StringBuilder buffer; LogAppendable(final Logger log, final String prefix) { this.log = log; this.prefix = prefix; this.buffer = new StringBuilder(); } @Override public Appendable append(final CharSequence text) throws IOException { return append(text, 0, text.length()); } @Override public Appendable append(final CharSequence text, final int start, final int end) throws IOException { for (int i = start; i < end; i++) { append(text.charAt(i)); } return this; } @Override public Appendable append(final char ch) throws IOException { if (ch == '\n') { log.debug("{} {}", prefix, buffer); buffer.setLength(0); } else if (ch != '\r') { buffer.append(ch); } return this; } public void flush() { if (buffer.length() > 0) { log.debug("{} {}", prefix, buffer); buffer.setLength(0); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LoggingIOSession.java0100664 0000000 0000000 00000024114 14245617503 027552 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.channels.SelectionKey; import java.util.concurrent.locks.Lock; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.Chars; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; @Internal public class LoggingIOSession implements IOSession { private final Logger log; private final Logger wireLog; private final IOSession session; public LoggingIOSession(final IOSession session, final Logger log, final Logger wireLog) { super(); this.session = session; this.log = log; this.wireLog = wireLog; } public LoggingIOSession(final ProtocolIOSession session, final Logger log) { this(session, log, null); } @Override public String getId() { return session.getId(); } @Override public Lock getLock() { return this.session.getLock(); } @Override public void enqueue(final Command command, final Command.Priority priority) { this.session.enqueue(command, priority); if (this.log.isDebugEnabled()) { this.log.debug("{} enqueued {} with priority {}", this.session, command.getClass().getSimpleName(), priority); } } @Override public boolean hasCommands() { return this.session.hasCommands(); } @Override public Command poll() { return this.session.poll(); } @Override public ByteChannel channel() { return this.session.channel(); } @Override public SocketAddress getLocalAddress() { return this.session.getLocalAddress(); } @Override public SocketAddress getRemoteAddress() { return this.session.getRemoteAddress(); } @Override public int getEventMask() { return this.session.getEventMask(); } private static String formatOps(final int ops) { final StringBuilder buffer = new StringBuilder(6); buffer.append('['); if ((ops & SelectionKey.OP_READ) > 0) { buffer.append('r'); } if ((ops & SelectionKey.OP_WRITE) > 0) { buffer.append('w'); } if ((ops & SelectionKey.OP_ACCEPT) > 0) { buffer.append('a'); } if ((ops & SelectionKey.OP_CONNECT) > 0) { buffer.append('c'); } buffer.append(']'); return buffer.toString(); } @Override public void setEventMask(final int ops) { this.session.setEventMask(ops); if (this.log.isDebugEnabled()) { this.log.debug("{} event mask set {}", this.session, formatOps(ops)); } } @Override public void setEvent(final int op) { this.session.setEvent(op); if (this.log.isDebugEnabled()) { this.log.debug("{} event set {}", this.session, formatOps(op)); } } @Override public void clearEvent(final int op) { this.session.clearEvent(op); if (this.log.isDebugEnabled()) { this.log.debug("{} event cleared {}", this.session, formatOps(op)); } } @Override public void close() { if (this.log.isDebugEnabled()) { this.log.debug("{} close", this.session); } this.session.close(); } @Override public Status getStatus() { return this.session.getStatus(); } @Override public boolean isOpen() { return session.isOpen(); } @Override public void close(final CloseMode closeMode) { if (this.log.isDebugEnabled()) { this.log.debug("{} shutdown {}", this.session, closeMode); } this.session.close(closeMode); } @Override public Timeout getSocketTimeout() { return this.session.getSocketTimeout(); } @Override public void setSocketTimeout(final Timeout timeout) { if (this.log.isDebugEnabled()) { this.log.debug("{} set timeout {}", this.session, timeout); } this.session.setSocketTimeout(timeout); } @Override public int read(final ByteBuffer dst) throws IOException { final int bytesRead = session.read(dst); if (log.isDebugEnabled()) { log.debug("{} {} bytes read", session, bytesRead); } if (bytesRead > 0 && wireLog.isDebugEnabled()) { final ByteBuffer b = dst.duplicate(); final int p = b.position(); b.limit(p); b.position(p - bytesRead); logData(b, "<< "); } return bytesRead; } @Override public int write(final ByteBuffer src) throws IOException { final int byteWritten = session.write(src); if (log.isDebugEnabled()) { log.debug("{} {} bytes written", session, byteWritten); } if (byteWritten > 0 && wireLog.isDebugEnabled()) { final ByteBuffer b = src.duplicate(); final int p = b.position(); b.limit(p); b.position(p - byteWritten); logData(b, ">> "); } return byteWritten; } private void logData(final ByteBuffer data, final String prefix) throws IOException { final byte[] line = new byte[16]; final StringBuilder buf = new StringBuilder(); while (data.hasRemaining()) { buf.setLength(0); buf.append(prefix); final int chunk = Math.min(data.remaining(), line.length); data.get(line, 0, chunk); for (int i = 0; i < chunk; i++) { final char ch = (char) line[i]; if (ch > Chars.SP && ch <= Chars.DEL) { buf.append(ch); } else if (Character.isWhitespace(ch)) { buf.append(' '); } else { buf.append('.'); } } for (int i = chunk; i < 17; i++) { buf.append(' '); } for (int i = 0; i < chunk; i++) { buf.append(' '); final int b = line[i] & 0xff; final String s = Integer.toHexString(b); if (s.length() == 1) { buf.append("0"); } buf.append(s); } wireLog.debug(buf.toString()); } } @Override public void updateReadTime() { this.session.updateReadTime(); } @Override public void updateWriteTime() { this.session.updateWriteTime(); } @Override public long getLastReadTime() { return this.session.getLastReadTime(); } @Override public long getLastWriteTime() { return this.session.getLastWriteTime(); } @Override public long getLastEventTime() { return this.session.getLastEventTime(); } @Override public IOEventHandler getHandler() { return this.session.getHandler(); } @Override public void upgrade(final IOEventHandler handler) { Args.notNull(handler, "Protocol handler"); if (this.log.isDebugEnabled()) { this.log.debug("{} protocol upgrade: {}", this.session, handler.getClass()); } this.session.upgrade(new IOEventHandler() { @Override public void connected(final IOSession protocolSession) throws IOException { handler.connected(protocolSession); } @Override public void inputReady(final IOSession protocolSession, final ByteBuffer src) throws IOException { if (src != null && wireLog.isDebugEnabled()) { final ByteBuffer b = src.duplicate(); logData(b, "<< "); } handler.inputReady(protocolSession, src); } @Override public void outputReady(final IOSession protocolSession) throws IOException { handler.outputReady(protocolSession); } @Override public void timeout(final IOSession protocolSession, final Timeout timeout) throws IOException { handler.timeout(protocolSession, timeout); } @Override public void exception(final IOSession protocolSession, final Exception cause) { handler.exception(protocolSession, cause); } @Override public void disconnected(final IOSession protocolSession) { handler.disconnected(protocolSession); } }); } @Override public String toString() { return this.session.toString(); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LoggingH2StreamListener.java0100664 0000000 0000000 00000012726 14315123013 031022 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http2.frame.FramePrinter; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.testing.classic.LoggingSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoggingH2StreamListener implements H2StreamListener { public final static LoggingH2StreamListener INSTANCE = new LoggingH2StreamListener(); private final Logger headerLog; private final Logger frameLog; private final Logger framePayloadLog; private final Logger flowCtrlLog; private final FramePrinter framePrinter; private LoggingH2StreamListener() { this.framePrinter = new FramePrinter(); this.headerLog = LoggerFactory.getLogger("org.apache.hc.core5.http.headers"); this.frameLog = LoggerFactory.getLogger("org.apache.hc.core5.http2.frame"); this.framePayloadLog = LoggerFactory.getLogger("org.apache.hc.core5.http2.frame.payload"); this.flowCtrlLog = LoggerFactory.getLogger("org.apache.hc.core5.http2.flow"); } private void logFrameInfo(final String prefix, final RawFrame frame) { try { final LogAppendable logAppendable = new LogAppendable(frameLog, prefix); framePrinter.printFrameInfo(frame, logAppendable); logAppendable.flush(); } catch (final IOException ignore) { // ignore } } private void logFramePayload(final String prefix, final RawFrame frame) { try { final LogAppendable logAppendable = new LogAppendable(framePayloadLog, prefix); framePrinter.printPayload(frame, logAppendable); logAppendable.flush(); } catch (final IOException ignore) { // ignore } } private void logFlowControl(final String prefix, final int streamId, final int delta, final int actualSize) { flowCtrlLog.debug("{} stream {} flow control {} -> {}", prefix, streamId, delta, actualSize); } @Override public void onHeaderInput(final HttpConnection connection, final int streamId, final List headers) { if (headerLog.isDebugEnabled()) { final String prefix = LoggingSupport.getId(connection); for (int i = 0; i < headers.size(); i++) { headerLog.debug("{} << {}", prefix, headers.get(i)); } } } @Override public void onHeaderOutput(final HttpConnection connection, final int streamId, final List headers) { if (headerLog.isDebugEnabled()) { final String prefix = LoggingSupport.getId(connection); for (int i = 0; i < headers.size(); i++) { headerLog.debug("{} >> {}", prefix, headers.get(i)); } } } @Override public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) { if (frameLog.isDebugEnabled()) { logFrameInfo(LoggingSupport.getId(connection) + " <<", frame); } if (framePayloadLog.isDebugEnabled()) { logFramePayload(LoggingSupport.getId(connection) + " <<", frame); } } @Override public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) { if (frameLog.isDebugEnabled()) { logFrameInfo(LoggingSupport.getId(connection) + " >>", frame); } if (framePayloadLog.isDebugEnabled()) { logFramePayload(LoggingSupport.getId(connection) + " >>", frame); } } @Override public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { if (flowCtrlLog.isDebugEnabled()) { logFlowControl(LoggingSupport.getId(connection) + " in", streamId, delta, actualSize); } } @Override public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { if (flowCtrlLog.isDebugEnabled()) { logFlowControl(LoggingSupport.getId(connection) + " out", streamId, delta, actualSize); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/AsyncRequester.java0100664 0000000 0000000 00000007736 14245617503 027360 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; public class AsyncRequester extends IOReactorExecutor implements ConnectionInitiator { public AsyncRequester(final IOReactorConfig ioReactorConfig) { super(ioReactorConfig, null); } @Override DefaultConnectingIOReactor createIOReactor( final IOEventHandlerFactory ioEventHandlerFactory, final IOReactorConfig ioReactorConfig, final ThreadFactory threadFactory, final Callback sessionShutdownCallback) throws IOException { return new DefaultConnectingIOReactor( ioEventHandlerFactory, ioReactorConfig, threadFactory, LoggingIOSessionDecorator.INSTANCE, LoggingExceptionCallback.INSTANCE, LoggingIOSessionListener.INSTANCE, sessionShutdownCallback); } private InetSocketAddress toSocketAddress(final HttpHost host) { int port = host.getPort(); if (port < 0) { final String scheme = host.getSchemeName(); if (URIScheme.HTTP.same(scheme)) { port = 80; } else if (URIScheme.HTTPS.same(scheme)) { port = 443; } } final String hostName = host.getHostName(); return new InetSocketAddress(hostName, port); } public Future requestSession(final HttpHost host, final Timeout timeout, final FutureCallback callback) { Args.notNull(host, "Host"); return reactor().connect(host, toSocketAddress(host), null, timeout, null, callback); } @Override public Future connect( final NamedEndpoint remoteEndpoint, final SocketAddress remoteAddress, final SocketAddress localAddress, final Timeout timeout, final Object attachment, final FutureCallback callback) { return reactor().connect(remoteEndpoint, remoteAddress, localAddress, timeout, attachment, callback); } } ././@LongLink0100644 0000000 0000000 00000000156 14403631147 011640 Lustar 0000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientProtocolNegotiationStarter.javahttpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientProtocolNegotiationSta0100664 0000000 0000000 00000013417 14403631147 032756 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import javax.net.ssl.SSLContext; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler; import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.impl.H2Processors; import org.apache.hc.core5.http2.impl.nio.ClientH2PrefaceHandler; import org.apache.hc.core5.http2.impl.nio.ClientH2StreamMultiplexerFactory; import org.apache.hc.core5.http2.impl.nio.ClientH2UpgradeHandler; import org.apache.hc.core5.http2.impl.nio.ClientHttp1UpgradeHandler; import org.apache.hc.core5.http2.impl.nio.HttpProtocolNegotiator; import org.apache.hc.core5.http2.ssl.ApplicationProtocol; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.util.Args; class InternalClientProtocolNegotiationStarter implements IOEventHandlerFactory { private final HttpProcessor httpProcessor; private final HandlerFactory exchangeHandlerFactory; private final HttpVersionPolicy versionPolicy; private final H2Config h2Config; private final Http1Config http1Config; private final CharCodingConfig charCodingConfig; private final SSLContext sslContext; private final SSLSessionInitializer sslSessionInitializer; private final SSLSessionVerifier sslSessionVerifier; InternalClientProtocolNegotiationStarter( final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final HttpVersionPolicy versionPolicy, final H2Config h2Config, final Http1Config http1Config, final CharCodingConfig charCodingConfig, final SSLContext sslContext, final SSLSessionInitializer sslSessionInitializer, final SSLSessionVerifier sslSessionVerifier) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.exchangeHandlerFactory = exchangeHandlerFactory; this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE; this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT; this.http1Config = http1Config != null ? http1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.sslContext = sslContext; this.sslSessionInitializer = sslSessionInitializer; this.sslSessionVerifier = sslSessionVerifier; } @Override public IOEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { if (sslContext != null) { ioSession.startTls(sslContext, null, null, sslSessionInitializer, sslSessionVerifier, null); } final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ClientHttp1StreamDuplexerFactory( httpProcessor != null ? httpProcessor : HttpProcessors.client(), http1Config, charCodingConfig, LoggingHttp1StreamListener.INSTANCE_CLIENT); final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory = new ClientH2StreamMultiplexerFactory( httpProcessor != null ? httpProcessor : H2Processors.client(), exchangeHandlerFactory, h2Config, charCodingConfig, LoggingH2StreamListener.INSTANCE); ioSession.registerProtocol(ApplicationProtocol.HTTP_1_1.id, new ClientHttp1UpgradeHandler(http1StreamHandlerFactory)); ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ClientH2UpgradeHandler(http2StreamHandlerFactory)); switch (versionPolicy) { case FORCE_HTTP_2: return new ClientH2PrefaceHandler(ioSession, http2StreamHandlerFactory, false); case FORCE_HTTP_1: return new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession)); default: return new HttpProtocolNegotiator(ioSession, null); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LoggingExceptionCallback.java0100664 0000000 0000000 00000003315 14245617503 031252 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import org.apache.hc.core5.function.Callback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoggingExceptionCallback implements Callback { public final static LoggingExceptionCallback INSTANCE = new LoggingExceptionCallback(); private final Logger log = LoggerFactory.getLogger("org.apache.hc.core5.reactor"); private LoggingExceptionCallback() { } @Override public void execute(final Exception ex) { log.error(ex.getMessage(), ex); } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/reactive/0040775 0000000 0000000 00000000000 14403631147 024537 5ustar000000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/reactive/ReactiveTestUtils.java0100664 0000000 0000000 00000014273 14403631147 031031 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.reactive; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.util.TextUtils; import org.reactivestreams.Publisher; import io.reactivex.Emitter; import io.reactivex.Flowable; import io.reactivex.Single; import io.reactivex.functions.Consumer; /** * @deprecated Use {@link Reactive3TestUtils} and RxJava3 */ @Deprecated public class ReactiveTestUtils { /** The range from which to generate random data. */ private final static byte[] RANGE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" .getBytes(StandardCharsets.US_ASCII); /** * Produces a deterministic stream of bytes, in randomly sized chunks of up to 128kB. * * @param length the number of bytes in the stream * @return a reactive stream of bytes */ public static Flowable produceStream(final long length) { return produceStream(length, null); } /** * Produces a deterministic stream of bytes, in randomly sized chunks of up to 128kB, while computing the hash of * the random data. * * @param length the number of bytes in the stream * @param hash an output argument for the hash, set when the end of the stream is reached; if {@code null}, the * hash will not be computed * @return a reactive stream of bytes */ public static Flowable produceStream(final long length, final AtomicReference hash) { return produceStream(length, 128 * 1024, hash); } /** * Produces a deterministic stream of bytes, in randomly sized chunks, while computing the hash of the random data. * * @param length the number of bytes in the stream * @param maximumBlockSize the maximum size of any {@code ByteBuffer in the stream} * @param hash an output argument for the hash, set when the end of the stream is reached; if {@code null}, the * hash will not be computed * @return a reactive stream of bytes */ public static Flowable produceStream( final long length, final int maximumBlockSize, final AtomicReference hash ) { return Flowable.generate(new Consumer>() { final Random random = new Random(length); // Use the length as the random seed for easy reproducibility long bytesEmitted; final MessageDigest md = newMessageDigest(); @Override public void accept(final Emitter emitter) { final long remainingLength = length - bytesEmitted; if (remainingLength == 0) { emitter.onComplete(); if (hash != null) { hash.set(TextUtils.toHexString(md.digest())); } } else { final int bufferLength = (int) Math.min(remainingLength, 1 + random.nextInt(maximumBlockSize)); final byte[] bs = new byte[bufferLength]; for (int i = 0; i < bufferLength; i++) { final byte b = RANGE[(int) (random.nextDouble() * RANGE.length)]; bs[i] = b; } if (hash != null) { md.update(bs); } emitter.onNext(ByteBuffer.wrap(bs)); bytesEmitted += bufferLength; } } }); } /** * Computes the hash of the deterministic stream (as produced by {@link #produceStream(long)}). */ public static String getStreamHash(final long length) { return TextUtils.toHexString(consumeStream(produceStream(length)).blockingGet().md.digest()); } /** * Consumes the given stream and returns a data structure containing its length and digest. */ public static Single consumeStream(final Publisher publisher) { final StreamDescription seed = new StreamDescription(0, newMessageDigest()); return Flowable.fromPublisher(publisher) .reduce(seed, (desc, byteBuffer) -> { final long length = desc.length + byteBuffer.remaining(); desc.md.update(byteBuffer); return new StreamDescription(length, desc.md); }); } private static MessageDigest newMessageDigest() { try { return MessageDigest.getInstance("MD5"); } catch (final NoSuchAlgorithmException ex) { throw new AssertionError(ex); } } public static class StreamDescription { public final long length; public final MessageDigest md; public StreamDescription(final long length, final MessageDigest md) { this.length = length; this.md = md; } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/reactive/ReactiveEchoProcessor.java0100664 0000000 0000000 00000005617 14245617503 031655 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.reactive; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.impl.BasicEntityDetails; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.reactive.ReactiveRequestProcessor; import org.reactivestreams.Publisher; public final class ReactiveEchoProcessor implements ReactiveRequestProcessor { public ReactiveEchoProcessor() { } @Override public void processRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context, final Publisher requestBody, final Callback> responseBodyFuture ) throws HttpException, IOException { if (new BasicHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE).equals(request.getHeader(HttpHeaders.EXPECT))) { responseChannel.sendInformation(new BasicHttpResponse(100), context); } responseChannel.sendResponse( new BasicHttpResponse(200), new BasicEntityDetails(-1, ContentType.APPLICATION_OCTET_STREAM), context); responseBodyFuture.execute(requestBody); } }httpcore5-testing/src/main/java/org/apache/hc/core5/testing/reactive/Reactive3TestUtils.java0100664 0000000 0000000 00000014326 14403631147 031113 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.reactive; import io.reactivex.rxjava3.core.Emitter; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.functions.Consumer; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.util.TextUtils; import org.reactivestreams.Publisher; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; import java.util.concurrent.atomic.AtomicReference; @Internal public class Reactive3TestUtils { /** The range from which to generate random data. */ private final static byte[] RANGE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" .getBytes(StandardCharsets.US_ASCII); /** * Produces a deterministic stream of bytes, in randomly sized chunks of up to 128kB. * * @param length the number of bytes in the stream * @return a reactive stream of bytes */ public static Flowable produceStream(final long length) { return produceStream(length, null); } /** * Produces a deterministic stream of bytes, in randomly sized chunks of up to 128kB, while computing the hash of * the random data. * * @param length the number of bytes in the stream * @param hash an output argument for the hash, set when the end of the stream is reached; if {@code null}, the * hash will not be computed * @return a reactive stream of bytes */ public static Flowable produceStream(final long length, final AtomicReference hash) { return produceStream(length, 128 * 1024, hash); } /** * Produces a deterministic stream of bytes, in randomly sized chunks, while computing the hash of the random data. * * @param length the number of bytes in the stream * @param maximumBlockSize the maximum size of any {@code ByteBuffer in the stream} * @param hash an output argument for the hash, set when the end of the stream is reached; if {@code null}, the * hash will not be computed * @return a reactive stream of bytes */ public static Flowable produceStream( final long length, final int maximumBlockSize, final AtomicReference hash ) { return Flowable.generate(new Consumer>() { final Random random = new Random(length); // Use the length as the random seed for easy reproducibility long bytesEmitted; final MessageDigest md = newMessageDigest(); @Override public void accept(final Emitter emitter) { final long remainingLength = length - bytesEmitted; if (remainingLength == 0) { emitter.onComplete(); if (hash != null) { hash.set(TextUtils.toHexString(md.digest())); } } else { final int bufferLength = (int) Math.min(remainingLength, 1 + random.nextInt(maximumBlockSize)); final byte[] bs = new byte[bufferLength]; for (int i = 0; i < bufferLength; i++) { final byte b = RANGE[(int) (random.nextDouble() * RANGE.length)]; bs[i] = b; } if (hash != null) { md.update(bs); } emitter.onNext(ByteBuffer.wrap(bs)); bytesEmitted += bufferLength; } } }); } /** * Computes the hash of the deterministic stream (as produced by {@link #produceStream(long)}). */ public static String getStreamHash(final long length) { return TextUtils.toHexString(consumeStream(produceStream(length)).blockingGet().md.digest()); } /** * Consumes the given stream and returns a data structure containing its length and digest. */ public static Single consumeStream(final Publisher publisher) { final StreamDescription seed = new StreamDescription(0, newMessageDigest()); return Flowable.fromPublisher(publisher) .reduce(seed, (desc, byteBuffer) -> { final long length = desc.length + byteBuffer.remaining(); desc.md.update(byteBuffer); return new StreamDescription(length, desc.md); }); } private static MessageDigest newMessageDigest() { try { return MessageDigest.getInstance("MD5"); } catch (final NoSuchAlgorithmException ex) { throw new AssertionError(ex); } } public static class StreamDescription { public final long length; public final MessageDigest md; public StreamDescription(final long length, final MessageDigest md) { this.length = length; this.md = md; } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/reactive/ReactiveRandomProcessor.java0100664 0000000 0000000 00000011431 14403631147 032202 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.reactive; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.MethodNotSupportedException; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.impl.BasicEntityDetails; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.reactive.ReactiveRequestProcessor; import org.reactivestreams.Publisher; import io.reactivex.rxjava3.core.Flowable; public class ReactiveRandomProcessor implements ReactiveRequestProcessor { public ReactiveRandomProcessor() { } @Override public void processRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context, final Publisher requestBody, final Callback> responseBodyCallback ) throws HttpException, IOException { final String method = request.getMethod(); if (!"GET".equalsIgnoreCase(method) && !"HEAD".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method) && !"PUT".equalsIgnoreCase(method)) { throw new MethodNotSupportedException(method + " not supported by " + getClass().getName()); } final URI uri; try { uri = request.getUri(); } catch (final URISyntaxException ex) { throw new ProtocolException(ex.getMessage(), ex); } final String path = uri.getPath(); final int slash = path.lastIndexOf('/'); if (slash != -1) { final String payload = path.substring(slash + 1); final long n; if (!payload.isEmpty()) { try { n = Long.parseLong(payload); } catch (final NumberFormatException ex) { throw new ProtocolException("Invalid request path: " + path); } } else { // random length, but make sure at least something is sent n = 1 + (int) (Math.random() * 79.0); } if (new BasicHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE).equals(request.getHeader(HttpHeaders.EXPECT))) { responseChannel.sendInformation(new BasicHttpResponse(100), context); } final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); final Flowable stream = Reactive3TestUtils.produceStream(n); final String hash = Reactive3TestUtils.getStreamHash(n); response.addHeader("response-hash-code", hash); final BasicEntityDetails basicEntityDetails = new BasicEntityDetails(n, ContentType.APPLICATION_OCTET_STREAM); responseChannel.sendResponse(response, basicEntityDetails, context); responseBodyCallback.execute(stream); } else { throw new ProtocolException("Invalid request path: " + path); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/0040775 0000000 0000000 00000000000 14403631147 024732 5ustar000000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientAdapter.java0100664 0000000 0000000 00000015613 14315123013 032447 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.testing.classic.ClassicTestClient; import org.apache.hc.core5.util.Timeout; public class ClassicTestClientAdapter extends ClientPOJOAdapter { /** * {@inheritDoc} */ @Override public Map execute(final String defaultURI, final Map request) throws Exception { // check the request for missing items. if (defaultURI == null) { throw new HttpException("defaultURL cannot be null"); } if (request == null) { throw new HttpException("request cannot be null"); } if (! request.containsKey(PATH)) { throw new HttpException("Request path should be set."); } if (! request.containsKey(METHOD)) { throw new HttpException("Request method should be set."); } final Timeout timeout; if (request.containsKey(TIMEOUT)) { timeout = Timeout.ofMilliseconds((long) request.get(TIMEOUT)); } else { timeout = null; } final ClassicTestClient client = new ClassicTestClient(SocketConfig.custom() .setSoTimeout(timeout) .build()); // Append the path to the defaultURI. String tempDefaultURI = defaultURI; if (! defaultURI.endsWith("/")) { tempDefaultURI += "/"; } final URI startingURI = new URI(tempDefaultURI + request.get(PATH)); final URI uri; // append each parameter in the query to the uri. @SuppressWarnings("unchecked") final Map queryMap = (Map) request.get(QUERY); if (queryMap != null) { final String existingQuery = startingURI.getRawQuery(); final StringBuilder newQuery = new StringBuilder(existingQuery == null ? "" : existingQuery); // append each parm to the query for (final Entry parm : queryMap.entrySet()) { newQuery.append("&").append(parm.getKey()).append("=").append(parm.getValue()); } // create a uri with the new query. uri = new URI( startingURI.getRawSchemeSpecificPart(), startingURI.getRawUserInfo(), startingURI.getHost(), startingURI.getPort(), startingURI.getRawPath(), newQuery.toString(), startingURI.getRawFragment()); } else { uri = startingURI; } final BasicClassicHttpRequest httpRequest = new BasicClassicHttpRequest(request.get(METHOD).toString(), uri); if (request.containsKey(PROTOCOL_VERSION)) { httpRequest.setVersion((ProtocolVersion) request.get(PROTOCOL_VERSION)); } // call addHeader for each header in headers. @SuppressWarnings("unchecked") final Map headersMap = (Map) request.get(HEADERS); if (headersMap != null) { for (final Entry header : headersMap.entrySet()) { httpRequest.addHeader(header.getKey(), header.getValue()); } } // call setEntity if a body is specified. final String requestBody = (String) request.get(BODY); if (requestBody != null) { final String requestContentType = (String) request.get(CONTENT_TYPE); final StringEntity entity = requestContentType != null ? new StringEntity(requestBody, ContentType.parse(requestContentType)) : new StringEntity(requestBody); httpRequest.setEntity(entity); } client.start(null); // Now start the request. final HttpHost host = new HttpHost(uri.getHost(), uri.getPort()); final HttpCoreContext context = HttpCoreContext.create(); try (final ClassicHttpResponse response = client.execute(host, httpRequest, context)) { // Prepare the response. It will contain status, body, headers, and contentType. final HttpEntity entity = response.getEntity(); final String body = entity == null ? null : EntityUtils.toString(entity); final String contentType = entity == null ? null : entity.getContentType(); // prepare the returned information final Map ret = new HashMap<>(); ret.put(STATUS, response.getCode()); // convert the headers to a Map final Map headerMap = new HashMap<>(); for (final Header header : response.getHeaders()) { headerMap.put(header.getName(), header.getValue()); } ret.put(HEADERS, headerMap); ret.put(BODY, body); ret.put(CONTENT_TYPE, contentType); return ret ; } } /** * {@inheritDoc} */ @Override public String getClientName() { return "ClassicTestClient"; } } ././@LongLink0100644 0000000 0000000 00000000145 14245617503 011642 Lustar 0000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkException.javahttpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkException.java0100664 0000000 0000000 00000005264 14245617503 032757 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; public class TestingFrameworkException extends Exception { public static final String NO_HTTP_CLIENT = "none"; private ClientTestingAdapter adapter; private FrameworkTest test; /** * */ private static final long serialVersionUID = -1010516169283589675L; /** * Creates a WebServerTestingFrameworkException with the specified detail message. */ public TestingFrameworkException(final String message) { super(message); } public TestingFrameworkException(final Throwable cause) { super(cause); } @Override public String getMessage() { String message = super.getMessage(); if (adapter != null) { final ClientPOJOAdapter pojoAdapter = adapter.getClientPOJOAdapter(); final String tempHttpClient = pojoAdapter == null ? null : pojoAdapter.getClientName(); final String httpClient = tempHttpClient == null ? NO_HTTP_CLIENT : tempHttpClient; if (message == null) { message = "null"; } message += "\nHTTP Client=" + httpClient; } if (test != null) { if (message == null) { message = "null"; } message += "\ntest:\n" + test; } return message; } public void setAdapter(final ClientTestingAdapter adapter) { this.adapter = adapter; } public void setTest(final FrameworkTest test) { this.test = test; } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java0100664 0000000 0000000 00000014422 14315123013 030360 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.BODY; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.CONTENT_TYPE; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.HEADERS; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.METHOD; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.PATH; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.PROTOCOL_VERSION; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.QUERY; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.REQUEST; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.RESPONSE; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.STATUS; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.net.URIBuilder; public class FrameworkTest { private Map request = new HashMap<>(); private Map response = new HashMap<>(); /** * Constructs a test with default values. */ public FrameworkTest() { this(null); } /** * Constructs a test with values that are passed in as well as defaults * for values that are not passed in. * * @param test Contains a REQUEST and an expected RESPONSE. * See {@link ClientPOJOAdapter} for details. */ @SuppressWarnings("unchecked") public FrameworkTest(final Map test) { if (test != null) { if (test.containsKey(REQUEST)) { request = (Map) test.get(REQUEST); } if (test.containsKey(RESPONSE)) { response = (Map) test.get(RESPONSE); } } } /** * Returns a request with defaults for any parameter that is not specified. * * @return a REQUEST map. * @throws TestingFrameworkException a problem such as an invalid URL */ public Map initRequest() throws TestingFrameworkException { // initialize to some helpful defaults final Map ret = new HashMap<>(); ret.put(PATH, TestingFramework.DEFAULT_REQUEST_PATH); ret.put(BODY, TestingFramework.DEFAULT_REQUEST_BODY); ret.put(CONTENT_TYPE, TestingFramework.DEFAULT_REQUEST_CONTENT_TYPE); ret.put(QUERY, new HashMap<>(TestingFramework.DEFAULT_REQUEST_QUERY)); ret.put(HEADERS, new HashMap<>(TestingFramework.DEFAULT_REQUEST_HEADERS)); ret.put(PROTOCOL_VERSION, TestingFramework.DEFAULT_REQUEST_PROTOCOL_VERSION); // GET is the default method. if (! request.containsKey(METHOD)) { request.put(METHOD, "GET"); } ret.putAll(request); moveAnyParametersInPathToQuery(ret); return ret; } private void moveAnyParametersInPathToQuery(final Map request) throws TestingFrameworkException { try { final String path = (String) request.get(PATH); if (path != null) { final URI uri = path.startsWith("/") ? new URI("http://localhost:8080" + path) : new URI("http://localhost:8080/"); final URIBuilder uriBuilder = new URIBuilder(uri, StandardCharsets.UTF_8); final List params = uriBuilder.getQueryParams(); @SuppressWarnings("unchecked") final Map queryMap = (Map) request.get(QUERY); for (final NameValuePair param : params) { queryMap.put(param.getName(), param.getValue()); } if (! params.isEmpty()) { request.put(PATH, uri.getPath()); } } } catch (final URISyntaxException e) { throw new TestingFrameworkException(e); } } /** * Returns an expected response with defaults for any parameter that is not specified. * * @return the RESPONSE map. */ public Map initResponseExpectations() { // 200 is the default status. if (! response.containsKey(STATUS)) { response.put(STATUS, 200); } final Map responseExpectations = new HashMap<>(); // initialize to some helpful defaults responseExpectations.put(BODY, TestingFramework.DEFAULT_RESPONSE_BODY); responseExpectations.put(CONTENT_TYPE, TestingFramework.DEFAULT_RESPONSE_CONTENT_TYPE); responseExpectations.put(HEADERS, new HashMap<>(TestingFramework.DEFAULT_RESPONSE_HEADERS)); // Now override any defaults with what is requested. responseExpectations.putAll(response); return responseExpectations; } @Override public String toString() { return "request: " + request + "\nresponse: " + response; } } ././@LongLink0100644 0000000 0000000 00000000153 14245617503 011641 Lustar 0000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientTestingAdapter.javahttpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClassicTestClientTestingAdapte0100664 0000000 0000000 00000002604 14245617503 032715 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; public class ClassicTestClientTestingAdapter extends ClientTestingAdapter { public ClassicTestClientTestingAdapter() { super(new ClassicTestClientAdapter()); } } ././@LongLink0100644 0000000 0000000 00000000152 14315123013 011622 Lustar 0000000 0000000 httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.javahttpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler0100664 0000000 0000000 00000027561 14315123013 033015 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.BODY; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.CONTENT_TYPE; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.HEADERS; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.METHOD; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.PROTOCOL_VERSION; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.QUERY; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.STATUS; import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIBuilder; public class TestingFrameworkRequestHandler implements HttpRequestHandler { protected Throwable thrown; protected Map requestExpectations; protected Map desiredResponse; /** * Sets the request expectations. * * @param requestExpectations the expected values of the request. * @throws TestingFrameworkException */ @SuppressWarnings("unchecked") public void setRequestExpectations(final Map requestExpectations) throws TestingFrameworkException { this.requestExpectations = (Map) TestingFramework.deepcopy(requestExpectations); } /** * Sets the desired response. The handler will return a response that matches this. * * @param desiredResponse the desired response. * @throws TestingFrameworkException */ @SuppressWarnings("unchecked") public void setDesiredResponse(final Map desiredResponse) throws TestingFrameworkException { this.desiredResponse = (Map) TestingFramework.deepcopy(desiredResponse); } /** * After the handler returns the response, any exception or failed assertion will be * in the member called "thrown". A testing framework can later call this method * which will rethrow the exception that was thrown before. * * @throws TestingFrameworkException */ public void assertNothingThrown() throws TestingFrameworkException { if (thrown != null) { final TestingFrameworkException e = (thrown instanceof TestingFrameworkException ? (TestingFrameworkException) thrown : new TestingFrameworkException(thrown)); thrown = null; throw e; } } /** *

Checks the HTTP request against the requestExpectations that it was previously given. * If there is a mismatch, an exception will be saved in the "thrown" member.

* *

Also, a response will be returned that matches the desiredResponse.

*/ @Override public void handle(final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { try { /* * Check the method against the method in the requestExpectations. */ final String actualMethod = request.getMethod(); final String expectedMethod = (String) requestExpectations.get(METHOD); if (! actualMethod.equals(expectedMethod)) { throw new TestingFrameworkException("Method not expected. " + " expected=" + expectedMethod + "; actual=" + actualMethod); } /* * Set the status to the status that is in the desiredResponse. */ final Object desiredStatus = desiredResponse.get(STATUS); if (desiredStatus != null) { response.setCode((int) desiredStatus); } /* * Check the query parameters against the parameters in requestExpectations. */ @SuppressWarnings("unchecked") final Map expectedQuery = (Map) requestExpectations.get(QUERY); if (expectedQuery != null) { final URI uri = request.getUri(); final URIBuilder uriBuilder = new URIBuilder(uri, StandardCharsets.UTF_8); final List actualParams = uriBuilder.getQueryParams(); final Map actualParamsMap = new HashMap<>(); for (final NameValuePair actualParam : actualParams) { actualParamsMap.put(actualParam.getName(), actualParam.getValue()); } for (final Map.Entry expectedParam : expectedQuery.entrySet()) { final String key = expectedParam.getKey(); if (! actualParamsMap.containsKey(key)) { throw new TestingFrameworkException("Expected parameter not found: " + key); } final String actualParamValue = actualParamsMap.get(key); final String expectedParamValue = expectedParam.getValue(); if (! actualParamValue.equals(expectedParamValue)) { throw new TestingFrameworkException("Expected parameter value not found. " + " Parameter=" + key + "; expected=" + expectedParamValue + "; actual=" + actualParamValue); } } } /* * Check the headers against the headers in requestExpectations. */ @SuppressWarnings("unchecked") final Map expectedHeaders = (Map) requestExpectations.get(HEADERS); if (expectedHeaders != null) { final Map actualHeadersMap = new HashMap<>(); final Header[] actualHeaders = request.getHeaders(); for (final Header header : actualHeaders) { actualHeadersMap.put(header.getName(), header.getValue()); } for (final Entry expectedHeader : expectedHeaders.entrySet()) { final String key = expectedHeader.getKey(); if (! actualHeadersMap.containsKey(key)) { throw new TestingFrameworkException("Expected header not found: " + key); } final String actualHeaderValue = actualHeadersMap.get(key); final String expectedHeaderValue = expectedHeader.getValue(); if (! actualHeaderValue.equals(expectedHeaderValue)) { throw new TestingFrameworkException("Expected header value not found. " + " Name=" + key + "; expected=" + expectedHeaderValue + "; actual=" + actualHeaderValue); } } } /* * Check the body. */ final String expectedBody = (String) requestExpectations.get(BODY); if (expectedBody != null) { final HttpEntity entity = request.getEntity(); final String data = EntityUtils.toString(entity); if (! data.equals(expectedBody)) { throw new TestingFrameworkException("Expected body not found. " + " Body=" + data + "; expected=" + expectedBody); } } /* * Check the contentType of the request. */ final String requestContentType = (String) requestExpectations.get(CONTENT_TYPE); if (requestContentType != null) { final HttpEntity entity = request.getEntity(); final String contentType = entity.getContentType(); final String expectedContentType = (String) requestExpectations.get(CONTENT_TYPE); if (! contentType.equals(expectedContentType)) { throw new TestingFrameworkException("Expected request content type not found. " + " Content Type=" + contentType + "; expected=" + expectedContentType); } } /* * Check the protocolVersion. */ if (requestExpectations.containsKey(PROTOCOL_VERSION)) { final ProtocolVersion protocolVersion = request.getVersion(); final ProtocolVersion expectedProtocolVersion = (ProtocolVersion) requestExpectations.get(PROTOCOL_VERSION); if (! protocolVersion.equals(expectedProtocolVersion)) { throw new TestingFrameworkException("Expected request protocol version not found. " + " Protocol Version=" + protocolVersion + "; expected=" + expectedProtocolVersion); } } /* * Return the body in desiredResponse using the contentType in desiredResponse. */ final String desiredBody = (String) desiredResponse.get(BODY); if (desiredBody != null) { final String desiredContentType = (String) desiredResponse.get(CONTENT_TYPE); final StringEntity entity = desiredContentType != null ? new StringEntity(desiredBody, ContentType.parse(desiredContentType)) : new StringEntity(desiredBody); response.setEntity(entity); } /* * Return the headers in desiredResponse. */ @SuppressWarnings("unchecked") final Map desiredHeaders = (Map) desiredResponse.get(HEADERS); if (desiredHeaders != null) { for (final Entry entry : desiredHeaders.entrySet()) { response.setHeader(entry.getKey(), entry.getValue()); } } } catch (final Throwable t) { /* * Save the throwable to be later retrieved by a call to assertNothingThrown(). */ thrown = t; } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java0100664 0000000 0000000 00000043565 14403631147 031102 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.BODY; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.CONTENT_TYPE; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.HEADERS; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.METHOD; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.REQUEST; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.RESPONSE; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.STATUS; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.protocol.UriPatternMatcher; import org.apache.hc.core5.io.CloseMode; public class TestingFramework { /** * Use the ALL_METHODS list to conveniently cycle through all HTTP methods. */ public static final List ALL_METHODS = Arrays.asList("HEAD", "GET", "DELETE", "POST", "PUT", "PATCH"); /** * If an {@link ClassicTestClientTestingAdapter} is unable to return a response in * the format this testing framework is needing, then it will need to check the * item in the response (such as body, status, headers, or contentType) itself and set * the returned value of the item as ALREADY_CHECKED. */ public static final Object ALREADY_CHECKED = new Object(); /** * If a test does not specify a path, this one is used. */ public static final String DEFAULT_REQUEST_PATH = "a/path"; /** * If a test does not specify a body, this one is used. */ public static final String DEFAULT_REQUEST_BODY = "{\"location\":\"home\"}"; /** * If a test does not specify a request contentType, this one is used. */ public static final String DEFAULT_REQUEST_CONTENT_TYPE = "application/json"; /** * If a test does not specify query parameters, these are used. */ public static final Map DEFAULT_REQUEST_QUERY; /** * If a test does not specify a request headers, these are used. */ public static final Map DEFAULT_REQUEST_HEADERS; /** * If a test does not specify a protocol version, this one is used. */ public static final ProtocolVersion DEFAULT_REQUEST_PROTOCOL_VERSION = HttpVersion.HTTP_1_1; /** * If a test does not specify an expected response status, this one is used. */ public static final int DEFAULT_RESPONSE_STATUS = 200; /** * If a test does not specify an expected response body, this one is used. */ public static final String DEFAULT_RESPONSE_BODY = "{\"location\":\"work\"}"; /** * If a test does not specify an expected response contentType, this one is used. */ public static final String DEFAULT_RESPONSE_CONTENT_TYPE = "application/json"; /** * If a test does not specify expected response headers, these are used. */ public static final Map DEFAULT_RESPONSE_HEADERS; static { final Map request = new HashMap<>(); request.put("p1", "this"); request.put("p2", "that"); DEFAULT_REQUEST_QUERY = Collections.unmodifiableMap(request); Map headers = new HashMap<>(); headers.put("header1", "stuff"); headers.put("header2", "more stuff"); DEFAULT_REQUEST_HEADERS = Collections.unmodifiableMap(headers); headers = new HashMap<>(); headers.put("header3", "header_three"); headers.put("header4", "header_four"); DEFAULT_RESPONSE_HEADERS = Collections.unmodifiableMap(headers); } private ClientTestingAdapter adapter; private TestingFrameworkRequestHandler requestHandler = new TestingFrameworkRequestHandler(); private List tests = new ArrayList<>(); private HttpServer server; private int port; public TestingFramework() throws TestingFrameworkException { this(null); } public TestingFramework(final ClientTestingAdapter adapter) throws TestingFrameworkException { this.adapter = adapter; /* * By default, a set of tests that will exercise each HTTP method are pre-loaded. */ for (final String method : ALL_METHODS) { final List statusList = Arrays.asList(200, 201); for (final Integer status : statusList) { final Map request = new HashMap<>(); request.put(METHOD, method); final Map response = new HashMap<>(); response.put(STATUS, status); final Map test = new HashMap<>(); test.put(REQUEST, request); test.put(RESPONSE, response); addTest(test); } } } /** * This is not likely to be used except during the testing of this class. * It is used to inject a mocked request handler. * * @param requestHandler */ public void setRequestHandler(final TestingFrameworkRequestHandler requestHandler) { this.requestHandler = requestHandler; } /** * Run the tests that have been previously added. First, an in-process {@link HttpServer} is * started. Then, all the tests are completed by passing each test to the adapter * which will make the HTTP request. * * @throws TestingFrameworkException if there is a test failure or unexpected problem. */ public void runTests() throws TestingFrameworkException { if (adapter == null) { throw new TestingFrameworkException("adapter should not be null"); } startServer(); try { for (final FrameworkTest test : tests) { try { callAdapter(test); } catch (final Throwable t) { processThrowable(t, test); } } } finally { stopServer(); } } private void processThrowable(final Throwable t, final FrameworkTest test) throws TestingFrameworkException { final TestingFrameworkException e; if (t instanceof TestingFrameworkException) { e = (TestingFrameworkException) t; } else { e = new TestingFrameworkException(t); } e.setAdapter(adapter); e.setTest(test); throw e; } private void startServer() throws TestingFrameworkException { /* * Start an in-process server and handle all HTTP requests * with the requestHandler. */ final SocketConfig socketConfig = SocketConfig.custom() .setSoTimeout(15000, TimeUnit.MILLISECONDS) .build(); final ServerBootstrap serverBootstrap = ServerBootstrap.bootstrap() .setLookupRegistry(new UriPatternMatcher<>()) .setSocketConfig(socketConfig) .register("/*", requestHandler); server = serverBootstrap.create(); try { server.start(); } catch (final IOException e) { throw new TestingFrameworkException(e); } port = server.getLocalPort(); } private void stopServer() { final HttpServer local = this.server; this.server = null; if (local != null) { local.close(CloseMode.IMMEDIATE); } } private void callAdapter(final FrameworkTest test) throws TestingFrameworkException { Map request = test.initRequest(); /* * If the adapter does not support the particular request, skip the test. */ if (! adapter.isRequestSupported(request)) { return; } /* * Allow the adapter to modify the request before the request expectations * are given to the requestHandler. Typically, adapters should not have * to modify the request. */ request = adapter.modifyRequest(request); // Tell the request handler what to expect in the request. requestHandler.setRequestExpectations(request); Map responseExpectations = test.initResponseExpectations(); /* * Allow the adapter to modify the response expectations before the handler * is told what to return. Typically, adapters should not have to modify * the response expectations. */ responseExpectations = adapter.modifyResponseExpectations(request, responseExpectations); // Tell the request handler what response to return. requestHandler.setDesiredResponse(responseExpectations); /* * Use the adapter to make the HTTP call. Make sure the responseExpectations are not changed * since they have already been sent to the request handler and they will later be used * to check the response. */ final String defaultURI = getDefaultURI(); final Map response = adapter.execute( defaultURI, request, requestHandler, Collections.unmodifiableMap(responseExpectations)); /* * The adapter is welcome to call assertNothingThrown() earlier, but we will * do it here to make sure it is done. If the handler threw any exception * while checking the request it received, it will be re-thrown here. */ requestHandler.assertNothingThrown(); assertResponseMatchesExpectation(request.get(METHOD), response, responseExpectations); } @SuppressWarnings("unchecked") private void assertResponseMatchesExpectation(final Object method, final Map actualResponse, final Map expectedResponse) throws TestingFrameworkException { if (actualResponse == null) { throw new TestingFrameworkException("response should not be null"); } /* * Now check the items in the response unless the adapter says they * already checked something. */ if (actualResponse.get(STATUS) != TestingFramework.ALREADY_CHECKED) { assertStatusMatchesExpectation(actualResponse.get(STATUS), expectedResponse.get(STATUS)); } if (! method.equals("HEAD")) { if (actualResponse.get(BODY) != TestingFramework.ALREADY_CHECKED) { assertBodyMatchesExpectation(actualResponse.get(BODY), expectedResponse.get(BODY)); } if (actualResponse.get(CONTENT_TYPE) != TestingFramework.ALREADY_CHECKED) { assertContentTypeMatchesExpectation(actualResponse.get(CONTENT_TYPE), expectedResponse.get(CONTENT_TYPE)); } } if (actualResponse.get(HEADERS) != TestingFramework.ALREADY_CHECKED) { assertHeadersMatchExpectation((Map) actualResponse.get(HEADERS), (Map) expectedResponse.get(HEADERS)); } } private void assertStatusMatchesExpectation(final Object actualStatus, final Object expectedStatus) throws TestingFrameworkException { if (actualStatus == null) { throw new TestingFrameworkException("Returned status is null."); } if ((expectedStatus != null) && (! actualStatus.equals(expectedStatus))) { throw new TestingFrameworkException("Expected status not found. expected=" + expectedStatus + "; actual=" + actualStatus); } } private void assertBodyMatchesExpectation(final Object actualBody, final Object expectedBody) throws TestingFrameworkException { if (actualBody == null) { throw new TestingFrameworkException("Returned body is null."); } if ((expectedBody != null) && (! actualBody.equals(expectedBody))) { throw new TestingFrameworkException("Expected body not found. expected=" + expectedBody + "; actual=" + actualBody); } } private void assertContentTypeMatchesExpectation(final Object actualContentType, final Object expectedContentType) throws TestingFrameworkException { if (expectedContentType != null) { if (actualContentType == null) { throw new TestingFrameworkException("Returned contentType is null."); } if (! actualContentType.equals(expectedContentType)) { throw new TestingFrameworkException("Expected content type not found. expected=" + expectedContentType + "; actual=" + actualContentType); } } } private void assertHeadersMatchExpectation(final Map actualHeaders, final Map expectedHeaders) throws TestingFrameworkException { if (expectedHeaders == null) { return; } for (final Map.Entry expectedHeader : expectedHeaders.entrySet()) { final String expectedHeaderName = expectedHeader.getKey(); if (! actualHeaders.containsKey(expectedHeaderName)) { throw new TestingFrameworkException("Expected header not found: name=" + expectedHeaderName); } if (! actualHeaders.get(expectedHeaderName).equals(expectedHeaders.get(expectedHeaderName))) { throw new TestingFrameworkException("Header value not expected: name=" + expectedHeaderName + "; expected=" + expectedHeaders.get(expectedHeaderName) + "; actual=" + actualHeaders.get(expectedHeaderName)); } } } private String getDefaultURI() { return "http://localhost:" + port + "/"; } /** * Sets the {@link ClientTestingAdapter}. * * @param adapter */ public void setAdapter(final ClientTestingAdapter adapter) { this.adapter = adapter; } /** * Deletes all tests. */ public void deleteTests() { tests = new ArrayList<>(); } /** * Call to add a test with defaults. * * @throws TestingFrameworkException */ public void addTest() throws TestingFrameworkException { addTest(null); } /** * Call to add a test. The test is a map with a REQUEST and a RESPONSE key. * See {@link ClientPOJOAdapter} for details on the format of the request and response. * * @param test Map with a REQUEST and a RESPONSE key. * @throws TestingFrameworkException */ @SuppressWarnings("unchecked") public void addTest(final Map test) throws TestingFrameworkException { final Map testCopy = (Map) deepcopy(test); tests.add(new FrameworkTest(testCopy)); } /** * Used to make a "deep" copy of an object. This testing framework makes deep copies * of tests that are added as well as requestExpectations Maps and response Maps. * * @param orig a serializable object. * @return a deep copy of the orig object. * @throws TestingFrameworkException */ public static Object deepcopy(final Object orig) throws TestingFrameworkException { try { // this is from http://stackoverflow.com/questions/13155127/deep-copy-map-in-groovy final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(orig); oos.flush(); final ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray()); final ObjectInputStream ois = new ObjectInputStream(bin); return ois.readObject(); } catch (final ClassNotFoundException | IOException ex) { throw new TestingFrameworkException(ex); } } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientPOJOAdapter.java0100664 0000000 0000000 00000013252 14245617503 031010 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import java.util.Map; /** * *

This adapter expects a request to be made up of POJOs such as Maps and Lists. In Groovy * the request could be expressed like this:

* *
 *
 * def request = [
 *                   path    : "a/path",
 *                   method  : "GET",
 *                   query   : [
 *                                parm1 : "1",
 *                                parm2 : "2",
 *                             ]
 *                   headers : [
 *                                header1 : "stuff",
 *                                header2 : "more_stuff",
 *                             ]
 *                   contentType : "application/json",
 *                   body        : '{"location" : "home" }',
 *                ]
 * 
* *

The adapter will translate this request into calls specific to a particular HTTP client.

* *

The response is then returned with POJOs with this structure:

* *
 *
 * def response = [
 *                    status      : 200,
 *                    headers     : [
 *                                      header1 : "response_stuff",
 *                                  ]
 *                    contentType : "application/json",
 *                    body        : '{"location" : "work" }',
 *                ]
 * 
* @since 5.0 */ public abstract class ClientPOJOAdapter { public static final String BODY = "body"; public static final String CONTENT_TYPE = "contentType"; public static final String HEADERS = "headers"; public static final String METHOD = "method"; public static final String NAME = "name"; public static final String PATH = "path"; public static final String PROTOCOL_VERSION = "protocolVersion"; public static final String QUERY = "query"; public static final String REQUEST = "request"; public static final String RESPONSE = "response"; public static final String STATUS = "status"; public static final String TIMEOUT = "timeout"; /** * Name of the HTTP Client that this adapter uses. * * @return name of the HTTP Client. */ public abstract String getClientName(); /** * Execute an HTTP request. * * @param defaultURI the URI used by default. The path in the request is * usually appended to it. * @param request the request as specified above. * * @return the response to the request as specified above. * * @throws Exception in case of a problem */ public abstract Map execute(String defaultURI, Map request) throws Exception; /** *

Check if a request is supported.

* *

Usually called directly by a testing framework. If an HTTP client does not support * a particular request, a non-null reason should be returned. Otherwise, if * the request is supported, return null.

* *

If this method is overridden, then the execute method should probably call * assertRequestSupported() at the beginning.

* * @param request the request as specified above. * * @return null if the request is supported; Otherwise, return a reason. */ public String checkRequestSupport(final Map request) { return null; } /** *

Assert that the request is supported

* *

Usually called by the execute method. Throws an exception if the request * is not supported.

* * @param request the request as specified above. * @throws Exception if the request is not supported. */ public void assertRequestSupported(final Map request) throws Exception { final String reason = checkRequestSupport(request); if (reason != null) { throw new Exception(reason); } } /** *

Modify the request.

* *

In a testing context, a testing framework can call this method to allow * the adapter to change the request. The request is then given to a * special request handler of the in-process HttpServer which will later check * an actual HTTP request against what is expected.

* *

In a production context, this is called by the execute method (if at all).

* * @param request the request as specified above. * @return the same request or a modification of it. */ public Map modifyRequest(final Map request) { return request; } } httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/ClientTestingAdapter.java0100664 0000000 0000000 00000015200 14245617503 031651 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import java.util.Map; public class ClientTestingAdapter { /** * This adapter will perform the HTTP request and return the response in the * expected format. */ protected ClientPOJOAdapter adapter; /* * The following is not expected to be changed to true, but it is to highlight * where the execute method can call the requestHandler's assertNothingThrown() * method if desired. Since this adapter's execute method does not check * the response, there is no need to call it. */ protected boolean callAssertNothingThrown; public ClientTestingAdapter() { } public ClientTestingAdapter(final ClientPOJOAdapter adapter) { this.adapter = adapter; } /** * See the documentation for the same method in {@link ClientPOJOAdapter}. This * method will typically call it. However, this method also has access to the * test's response expectations if that is needed for some reason. Furthermore, * this method also has access to the {@link TestingFrameworkRequestHandler} so * it can optionally call assertNothingThrown() before checking the response * further. It is optional because the test framework will call it later. * * @param defaultURI See execute method of {@link ClientPOJOAdapter}. * @param request See execute method of {@link ClientPOJOAdapter}. * @param requestHandler The request handler that checks the received HTTP request * with the request that was intended. If there is a * mismatch of expectations, then the requestHandler will * throw an exception. If this execute method does not want * to make further checks of the response in the case * the responseHandler threw, then the assertNothingThrown() * method should be called before doing further checks. * @param responseExpectations The response expectations of the test. * @return See return of the execute method of {@link ClientPOJOAdapter}. * @throws TestingFrameworkException in the case of a problem. */ public Map execute(final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { try { if (adapter == null) { throw new TestingFrameworkException("adapter cannot be null"); } // Call the adapter's execute method to actually make the HTTP request. final Map response = adapter.execute(defaultURI, request); /* * Adapters may call assertNothingThrown() if they would like. This would be to * make sure the following code is not executed in the event there was something * thrown in the request handler. * * Otherwise, the framework will call it when this method returns. So, it is * optional. */ if (callAssertNothingThrown) { if (requestHandler == null) { throw new TestingFrameworkException("requestHandler cannot be null"); } requestHandler.assertNothingThrown(); } return response; } catch (final TestingFrameworkException e) { throw e; } catch (final Exception ex) { throw new TestingFrameworkException(ex); } } /** * See the documentation for the same method in {@link ClientPOJOAdapter}. */ public boolean isRequestSupported(final Map request) { return (adapter == null) || adapter.checkRequestSupport(request) == null; } /** * See the documentation for the same method in {@link ClientPOJOAdapter}. */ public Map modifyRequest(final Map request) { return (adapter == null) ? request : adapter.modifyRequest(request); } /** * Generally a test's response expectations should not need to be modified, but * if a particular HTTP client (such as Groovy's RESTClient which uses HttpClient) * needs to modify the response expectations, it should do so here. After this * method returns, the {@link TestingFrameworkRequestHandler} is sent the * expectations so the request handler will return a response that matches the * expectations. When the HTTP response is obtained, the received response * is matched against the expectations. * * @param request for the format, see the documentation for {@link ClientPOJOAdapter}. * @param responseExpectations for the format, see the documentation for {@link ClientPOJOAdapter}. * @return the same or modified response expectations. */ public Map modifyResponseExpectations(final Map request, final Map responseExpectations) { return responseExpectations; } /** * Getter for the {@link ClientPOJOAdapter} that is actually used to make the * HTTP request. * * @return the {@link ClientPOJOAdapter}. */ public ClientPOJOAdapter getClientPOJOAdapter() { return adapter; } } httpcore5-testing/src/test/0040775 0000000 0000000 00000000000 14245617503 014757 5ustar000000000 0000000 httpcore5-testing/src/test/resources/0040775 0000000 0000000 00000000000 14442304153 016762 5ustar000000000 0000000 httpcore5-testing/src/test/resources/test-server.p120100664 0000000 0000000 00000011717 14245617503 021604 0ustar000000000 0000000 00 *H uq0m0i *H ZV0R0N *H  00) *H  0Mi"V/ƉPju)f)0t~{Y|xJa7A[zJ(6st_^C 6/ApLJcG jOa Vzص3w|Wu7Xz0M~G7#{/Lp[tK_ V%Kb.E݉o;6Z*9 bYeZ.&ύ).*tXa,7ږ] ridHy4eMYaQ?>Vf> "nLu\}r!7Cm$d˰\\hѴKXZFQ3SR٣˜-!9p/.~mT7iLX-cI8-=5ʲ鈺5aeܹyIBTJyMOYѿ k)1`AfDGC1IgT5WdoWdboj& ;ϨE#Ra#V{_!1h\1݁79M"PG*%Q;_>5JQLÔȐh>\@?[NJ04R~ivmʷZ}Ŋ7sHtsMK'ge;e' c}>zC([bdu$ONjԫ8k-t3ڡUC6>|]l!#ݜMԸ>͞%Ty4Eg\s3j#;( ?96F-f<)w ilr^`: /{"zQN6:ƭ1@0 *H  1 server0! *H  1Time 15708094235680  *H  0 0  *H 0) *H  0U* nJؼ,+#P zI%?$Rx{Rnʔ^DĠW/Զ~s&"[Ofslc[#^,"*5T~sd w|E$QLߔ4hg4XߒQпp{EQ2UR*x2IwsOF@3Q}DD:?<^ 6aV '$~2+;:4;^XjK.2~uۻє6K붴IC"iKŕ*`!Q,\|&w3/SlS;_Dq7敀ls3/q*H>4 @'_;nc!;W6¿,Vps"r:'\k$ay3g%&!Ho;z_i[kCO|C9Qui@?tk=QT扽`3c FNHcD+g8iݕĮ`cز: _8et QG٣~ᙼ%\k3h'zqgY:s FX48Ujָg/Q8 :&B=d 704#-$E-v"rU Ž,mD_;6d)hN9hrKt^|] @Z [~`gΕă@|q/Vfg:)gC9SkmZXE:j ؚGo7 p4fk#Oļfcb ifit@7:/i(~`ğ^rhOB(DNvpm%xݯA%V+?+ yX*r^ TѠ vJek,g`u֜/} % qdtAB6#L2ΎeJaǫF쁊Fgo Vz!@4j-:85 c۷`t$7pcNo b9A)@*2w/]Tͮp=&=QHWegV#2'\4AcK k.ʦ] .l5wF9$!pN!Vu9αy$=%uzA NtK^Qq mc,5,怘jNѓ0-*7)X=~DjTmrQC8Y h]~st\.s9 ѷ|V~9M`yߏ]ú[=dI 5>G8ȺI0_S WW:Enc" uklmXMgc18 XrL)8p(Q&p?$!p?=lƧdrfV$ >Xʿ?CZx̿fv`z/aTE{vJJkZ`Դzb9-qܖ?{v?;SUɾ]BGk~9b@O>]c( }PKf)9qN<$ Cf[dWR`X|,yp}e$w|ʕ~H,mg0 D\=,ho:M<p U-I%NJcܙ1`>2iH{f_9 IQZ)C4(”DO6npє)6SfQr$ztmẵ2b&cO餡&j/npb& ^1 yXjnU"?,!? &lS/9~+u6 .ThiͬpiιC|f~GW ESf su 碞^OA >qC{8詯'YEṟXZ+ϋ1OJFdi%^çtQ㽮ztrQ@3YU8ܺq9C,AYĩ-)*bRO&վd ac&"XeF=m` |? `hp,w_~'$51vWf/ xTy:>FQE:n̈́~)yIvY]~,I;ٴm5M3<6S!qkegjqmsX_G}Gy USW n-y(E[eHEtD oGptC?W4IP|B&,OXVM3e||8I5fV/kuĊd$w.؍fXNο7G%(ja_pPKRm_!K"^ h~>[}`WjwХP2!wTv!)JgВd~q6q^JB>h-Ͷhng޹&~O=j:EA85KoU|4[f'm./Hkul @eB 7ox'ϙm\3!L&F^(2{Q|Dgk-uVSeܞE{?l- iP1ߘM\-ty0qCS #vFCn?\f8͇ ^m XeW#qv߇`XdPz"2tXlGB/W=`թ"BCTѯ{4 * I欯=HW[0_Vg{4]4SŒToYQ r^:^=XQKFAC.2` &H wj<` ݲ~K8b<1oe\"5me~LчUb.4&+TօMݼƀ5Կc[+UV20b Y0>0!0 + (5df|N(A?` ]dhttpcore5-testing/src/test/resources/test.p120100664 0000000 0000000 00000005077 14245617503 020302 0ustar000000000 0000000 0 ;0  *H   0 0 *H rn0j0f *H  00) *H  0`4ALI2vP)Thʡ{c'$jTkN|Gh^o#qA+kw|]"ơQ`'^Ja#6"y?1CR CS!Hn _77hJ_G%(`GI?C?¤GS<#9w?_&TXf(qKW9Z~ppy0Sa3/Jnj;QxcKъ^Lbӂ @[\Q"+˻2lt^j~qNSz24T_ )f"wW-P>bȻ`&., tq[S_ԇڍ1E+]Զ]LtdoVZf-@|`T#|nf:D16[y^|`qVC^OV(dQ]gh8!ɊcNCSһK*&'G# }xPc;cv9jT/`x3JG'gO`$γE(lp`Kߡl^ظ:Ⲑ%~7C ^#/oF] :6E//zcBkViEd"vb$)|UF͗%# "ASEȵ24`t: jFrٞ@ D0|aANvlWzz'g]v~&)]cWlCǼ Ū?GdDdɑR8ڄX=/ xpS1ܢN%ߋх"7'o#U^)em:YwfH'ӽ3 !<PNh >\/#+K -{;h+Gʈnם6U<\7-눌C$gJ7wطi??WF)l9οpo}\CAz1 ZW-f!ʰbT 2-=$@8͚#h,Æ:W:6zZDJ$3/FF\&S /+`QU A197 l4e.54{wBJi,1X03 *H  1&$simple-http-server0! *H  1Time 15708083005120T *H E0A0: *H 0) *H  0:Cztr;!K ,Pe4-3m!DwހZBݪa5W ݲF;%C 0F}B2g.2S)V㊲_m qWQ+`cf0Y{ <ӌzDt@lO& CNJ) Ssʗh*R9iQ ]3kZ 7(Kl#GCc/q*~aYHLy+Z5Bpn\-/~cQ5ڴ9IcZ#6EғB&_t3IӼTP,J[ D0c`t2;5F4$uERui&tﳹ74<~jTmseѵipi7 zvc}Q!SrZKgA}j~`dxzMXr ҷ> |N.d .sc?9>1ԛÃmG}[@'v&X)"S@q>dzD 1x$$R{ꯀFt4hApf>r.|KcJ;r 0t!Vtip ѤK/n)( Eiv*0T9BѦ}QlPNKjlYBM y!|ҰL'CSi 0!0 +Hb;`wA|˪,q41xcPdBOڛ2httpcore5-testing/src/test/resources/log4j2-debug.xml.template0100664 0000000 0000000 00000003125 14245617503 023510 0ustar000000000 0000000 httpcore5-testing/src/test/resources/log4j2.xml0100664 0000000 0000000 00000002306 14442304153 020603 0ustar000000000 0000000 httpcore5-testing/src/test/resources/test-client.p120100664 0000000 0000000 00000021215 14245617503 021546 0ustar000000000 0000000 0"0"B *H "3"/0"+0  *H   0 0P *H  00) *H  0/}Հz]O{FP3Wg. AdJHGM[ζ-He/ѵ͜tb e h8+E2ް=^EDyRL^9w}&oN'+ᾠ+gCBś#>@b &DsUax~m0&R.2xÇi08h@[n0̀b N=\#D1J yV kMLR#rwm$BΦUݦ`]"Ē*\y6w(Q":ظhs Ԫ!=Dc1B0 *H  1client10! *H  1Time 15708095392920P *H  00) *H  0N7o35W4LP8w9c(t&_&3l/E'h'şQ~3ҬTDkf"I{g4&Ehdĸ pz >#py~.Yz5rH,%J}SP)^༶Q5MvpVpbʮ|t@ieC]>:8Tvr{69ܸ9c5!ܝz |!Hr5S2A"G΄-nq^XEKnC! YWJCӖf2KA!(@T'](> Ը v/c{ a pLuD10KBeFbaڏ 5L m1.xL>(%;iBc=Z-IJ_c*B XA2N9ͩԃ}–`[Ba!h7lZPe3%\,<સ`/G 'BWMSٱCF|98@~sYW?1˴3r@_E{U'7eQg+aClK8v XIpp[R-2J9,T{H+A M&F tR頨&$*;2K؏< qRmIDWg,"ku4ĀyFHh-f?`3@#W`F1IrE޽{xb9v8Pa~G{VivhxM.˜iy^W6wȽatf c[WS@<:GdMA/VO\A5SI5S [Z הH&.s:?}+{8 /Mr–]&Nl@ݱ]LNio2;jxǂOјg;e" uRh7Ht{ᗵMMSXR:GVmMV܏ͻfɍ,e*+1`JTI]ߗ__yw (5h/f2B 47"hi)r.~S?v[-Wӊpt4kfW r*P(j޺P*AY޳dFy}!֏ҙuxjܰa[O^fcnpwG0Uì1B0 *H  1client20! *H  1Time 15708095451480d *H U0Q0J *H 0) *H  0xykʡR^(4XoEPVtn]2%IAK` \ZeV˞WAY =}(.&^)؍`>7*ŎM sH=>%='Vz<1b<|P p6w$ 0a̮p.4x&H&Y/1͎ۏe_5c0eSs'۷"__L1LbtaS'KpVB:PZ{^ /KsleY0#q09 !F.F5t+o09.4`I$Ђ qۭ ĄGtnؠ ʅS߹:*RLͺ EXp8_W8q_b-a&ru):Jn)E{9s4{+)-5rLL<.`O䰘&<ӳܸv,Ҝ(PN}&=b5w~NBCwrI,Lۦf`Rۙ=|̊Sá {d@HӝןM'w7%~K t\/N.:$z]/3&mTunnŁ#f84 O]'J^L;ֲx!M!;Gå#?!Ǹ֯'ΆUBpxm"MfhZI12S MDrt?]ujP(E䛇&$\/+M4S4Pط^VsJO:KH<4OYskêdӑ3OPJg8 IA+0uix=^*e!PCmDФ9&ʰITc|/3+~gD3~}6ƀ }d-7K15_Ri:1z*oFx>\?ڱxEء{|BD(WPLS`g}QNH52$AOs")[|_ngq3%لWD JцbiTɇDezOӋWa"Ƀ 6~H,f](nvG¸+|hW"qR({|us’Z4U $0; :}m0pJx (ߠɽn*{btrο";MA┟kyf*aRT XOUS~[Mwsr xGp}N((1}a\8ټcBӁ9v&*X.14қ`8L˲Hlǚ,y$UR?ԋڧ[t3NaX2l(Ø%—m|fs/$>m\$;Lb쫯&b5l?{1h29Aq~W6CG:]D`N";Z_(4[Tې_"LP/K,g&,x@OP{ <{KPT~+1L ʑ!&U!U &ߗg]@-b<4/3d`W:3B|`1ձ}>0ꗧ~&ƌz v5nzo[s];tV應Ѻ1hor2AR48Xb҈lW1!{膩$O Y|g9 +U6Gc\} sN~v/$Le^ߪN- CŔD1i\vPg z4b \]NS=MwIKZKv $M1bPZdztlEگϲEF'5U}Ѻi'oM47Az.' zo%9^I+nJz}%h7MߵHvˋgTě#+|Uѥo֞ [ͭeY 2B%slYSߝИ"0b'04̂u9`39F' ,/. c.0PuhOJRԧۯ+=s ",nbYdcnt{%?d)Z(ް +›mbMXyQ|bLmTTKgpiĖH _?PG6R'iGBzoCvU oK\]%.)u :x?J-~ǡYKJRIZT=w]tΤs;~BspmH)RRe'Q0~dr&Cjz^gڟ?d,_'yh~ne^5!o9yIO@:vmuRYZ);&&\H^σMɛc}G EݖPOiimԟ;%kлf J#P٦OOğ\B61&zZс*m|wtiԂ0BXTŲV歱3TF}vx9̷BlFL,ZG7"[!ªn\lc,kgْdMPmv Z(>hO)"I܈vQsͫ1^c8"GwǮJpRxo֟۞Z- oE?KvTu]@;gJ\ꑙ.#ԻnY'P_9AڲA !9$cqτbK@3Uk]@c+缶 W1CbXTu~ fPR afT~R:bi} RVgtAط98Jo:QvwUV45{ h/V *ٍjO2?>U9t/-&P0\3`Ǎc Y09Hwj`f񀫪b[L_ DKȸA4}ay)?1ϴ?L:sK.4՟Z2P#A WoEPJ9b]mal˝H3 .]ktcKc׬Iy풌l0к68^HWH|`v3 vA$;DJ4@Q4~mxcM;|A Ӟdzd|y&Yܮ\+HFO᭧p| c$kpo2g  M$ӶRϓOWu`^zIA[-sw ЃnA>Qծa朦::S U+.{NePa@/P" /H<[:ފJ^^>'q3Ny ?Ir\+eE^59p)[=kqYglMP9wP-ǁui虱p$D>O$j۩ M jZݯ/,2VlGߺ7.S@.*pi?ȸbv۲uo Ls~Z|7Y#ڹ&z-S?K1ITUEìIZ+S.|fYkLO;_vTv'͟(,%kP])fSh=T%@cZ6ʳhRr\B= xX)Q`o-C,qCL`8I"yDyU\J_tD{Q]54w6rNn_MYnߙ-01l0>0!0 +4}s?2!3M:VH5ֵhttpcore5-testing/src/test/java/0040775 0000000 0000000 00000000000 14245617503 015700 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/0040775 0000000 0000000 00000000000 14245617503 016467 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/0040775 0000000 0000000 00000000000 14245617503 017710 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/0040775 0000000 0000000 00000000000 14245617503 020302 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/0040775 0000000 0000000 00000000000 14245617503 021317 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/benchmark/0040775 0000000 0000000 00000000000 14403631147 023245 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/benchmark/BenchmarkToolTest.java0100664 0000000 0000000 00000014151 14403631147 027477 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.benchmark; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.Future; import java.util.stream.Stream; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.entity.DiscardingEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncResponseBuilder; import org.apache.hc.core5.http.nio.support.BasicRequestConsumer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2ServerBootstrap; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; public class BenchmarkToolTest { public static Stream protocols() { return Stream.of( Arguments.of(HttpVersionPolicy.NEGOTIATE), Arguments.of(HttpVersionPolicy.FORCE_HTTP_2) ); } private HttpAsyncServer server; private InetSocketAddress address; public void setup(final HttpVersionPolicy versionPolicy) throws Exception { server = H2ServerBootstrap.bootstrap() .register("/", new AsyncServerRequestHandler>() { @Override public AsyncRequestConsumer> prepare( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) throws HttpException { return new BasicRequestConsumer<>(entityDetails != null ? new DiscardingEntityConsumer<>() : null); } @Override public void handle( final Message requestObject, final ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { responseTrigger.submitResponse( AsyncResponseBuilder.create(HttpStatus.SC_OK) .setEntity("0123456789ABCDEF") .build(), context); } }) .setVersionPolicy(versionPolicy) .create(); server.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTP); final ListenerEndpoint listener = future.get(); address = (InetSocketAddress) listener.getAddress(); } @AfterEach public void shutdown() throws Exception { if (server != null) { server.close(CloseMode.IMMEDIATE); } } @ParameterizedTest(name = "{0}") @MethodSource("protocols") public void testBasics(final HttpVersionPolicy versionPolicy) throws Exception { setup(versionPolicy); final BenchmarkConfig config = BenchmarkConfig.custom() .setKeepAlive(true) .setMethod(Method.POST.name()) .setPayloadText("0123456789ABCDEF") .setUri(new URIBuilder() .setScheme(URIScheme.HTTP.id) .setHost("localhost") .setPort(address .getPort()) .build()) .setConcurrencyLevel(3) .setForceHttp2(versionPolicy == HttpVersionPolicy.FORCE_HTTP_2) .setRequests(100) .build(); final HttpBenchmark httpBenchmark = new HttpBenchmark(config); final Results results = httpBenchmark.execute(); Assertions.assertNotNull(results); Assertions.assertEquals(100, results.getSuccessCount()); Assertions.assertEquals(0, results.getFailureCount()); Assertions.assertEquals(16, results.getContentLength()); Assertions.assertEquals(3, results.getConcurrencyLevel()); Assertions.assertEquals(100 * 16, results.getTotalContentBytesRecvd()); if (versionPolicy == HttpVersionPolicy.FORCE_HTTP_2) { Assertions.assertEquals(HttpVersion.HTTP_2, results.getProtocolVersion()); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/benchmark/ResultFormatterTest.java0100664 0000000 0000000 00000006604 14403631147 030115 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.benchmark; import static org.hamcrest.MatcherAssert.assertThat; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.HttpVersion; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; public class ResultFormatterTest { @Test public void testBasics() throws Exception { final Results results = new Results( "TestServer/1.1", HttpVersion.HTTP_1_1, "localhost", 8080, "/index.html", 2924, 5, 3399, 20000, 0, 20000, 62640000, 0, 50000000); final ByteArrayOutputStream buf = new ByteArrayOutputStream(); ResultFormatter.print(new PrintStream(buf, true, StandardCharsets.US_ASCII.name()), results); assertThat(new String(buf.toByteArray(), StandardCharsets.US_ASCII).replace("\r\n", "\n"), CoreMatchers.equalTo( "Server Software:\t\tTestServer/1.1\n" + "Protocol version:\t\tHTTP/1.1\n" + "Server Hostname:\t\tlocalhost\n" + "Server Port:\t\t\t8080\n" + "Document Path:\t\t\t/index.html\n" + "Document Length:\t\t2924 bytes\n" + "\n" + "Concurrency Level:\t\t5\n" + "Time taken for tests:\t3.399000 seconds\n" + "Complete requests:\t\t20000\n" + "Failed requests:\t\t0\n" + "Kept alive:\t\t\t\t20000\n" + "Total transferred:\t\t62640000 bytes\n" + "Content transferred:\t50000000 bytes\n" + "Requests per second:\t5,884.08 [#/sec] (mean)\n" + "Time per request:\t\t0.850 [ms] (mean)\n" + "Time per request:\t\t0.170 [ms] (mean, across all concurrent requests)\n" + "Transfer rate:\t\t\t17,997.02 [Kbytes/sec] received\n" )); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/0040775 0000000 0000000 00000000000 14403631147 022770 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/0040775 0000000 0000000 00000000000 14435411677 024422 5ustar000000000 0000000 ././@LongLink0100644 0000000 0000000 00000000161 14403631147 011634 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicHttp1SocksProxyCoreTransportTest.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicHttp1SocksProxyCoreTransp0100664 0000000 0000000 00000011013 14403631147 032675 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.StandardFilter; import org.apache.hc.core5.http.io.HttpFilterChain; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.testing.classic.extension.HttpRequesterResource; import org.apache.hc.core5.testing.classic.extension.HttpServerResource; import org.apache.hc.core5.testing.extension.SocksProxyResource; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class ClassicHttp1SocksProxyCoreTransportTest extends ClassicHttpCoreTransportTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension @Order(-Integer.MAX_VALUE) private final SocksProxyResource proxyResource; @RegisterExtension private final HttpServerResource serverResource; @RegisterExtension private final HttpRequesterResource clientResource; public ClassicHttp1SocksProxyCoreTransportTest(final URIScheme scheme) { super(scheme); this.proxyResource = new SocksProxyResource(); this.serverResource = new HttpServerResource(scheme, bootstrap -> bootstrap .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .register("*", new EchoHandler()) .addFilterBefore(StandardFilter.MAIN_HANDLER.name(), "no-keep-alive", (request, responseTrigger, context, chain) -> chain.proceed(request, new HttpFilterChain.ResponseTrigger() { @Override public void sendInformation( final ClassicHttpResponse response) throws HttpException, IOException { responseTrigger.sendInformation(response); } @Override public void submitResponse( final ClassicHttpResponse response) throws HttpException, IOException { if (request.getPath().startsWith("/no-keep-alive")) { response.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); } responseTrigger.submitResponse(response); } }, context))); this.clientResource = new HttpRequesterResource(bootstrap -> bootstrap .setSocketConfig(SocketConfig.custom() .setSocksProxyAddress(proxyResource.proxy().getProxyAddress()) .setSoTimeout(TIMEOUT) .build())); } @Override HttpServer serverStart() throws IOException { return serverResource.start(); } @Override HttpRequester clientStart() throws IOException { return clientResource.start(); } } ././@LongLink0100644 0000000 0000000 00000000152 14403631147 011634 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicServerBootstrapFilterTest.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicServerBootstrapFilterTest0100664 0000000 0000000 00000012064 14403631147 033010 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.io.HttpFilterChain; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.testing.classic.extension.HttpRequesterResource; import org.apache.hc.core5.testing.classic.extension.HttpServerResource; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class ClassicServerBootstrapFilterTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); private final URIScheme scheme; @RegisterExtension private final HttpServerResource serverResource; @RegisterExtension private final HttpRequesterResource clientResource; public ClassicServerBootstrapFilterTest(final URIScheme scheme) { this.scheme = scheme; this.serverResource = new HttpServerResource(scheme, bootstrap -> bootstrap .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .register("*", new EchoHandler()) .addFilterLast("test-filter", (request, responseTrigger, context, chain) -> chain.proceed(request, new HttpFilterChain.ResponseTrigger() { @Override public void sendInformation( final ClassicHttpResponse response) throws HttpException, IOException { responseTrigger.sendInformation(response); } @Override public void submitResponse( final ClassicHttpResponse response) throws HttpException, IOException { response.setHeader("X-Test-Filter", "active"); responseTrigger.submitResponse(response); } }, context))); this.clientResource = new HttpRequesterResource(bootstrap -> bootstrap .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build())); } @Test public void testFilters() throws Exception { final HttpServer server = serverResource.start(); final HttpRequester requester = clientResource.start(); server.start(); final HttpHost target = new HttpHost(scheme.id, "localhost", server.getLocalPort()); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/filters"); request.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response = requester.execute(target, request, TIMEOUT, context)) { assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final Header testFilterHeader = response.getHeader("X-Test-Filter"); assertThat(testFilterHeader, CoreMatchers.notNullValue()); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/EchoHandler.java0100664 0000000 0000000 00000004475 14245617503 027443 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.protocol.HttpContext; public class EchoHandler implements HttpRequestHandler { @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { response.setCode(HttpStatus.SC_OK); final HttpEntity entity = request.getEntity(); final byte[] data; if (entity != null) { data = EntityUtils.toByteArray(entity); response.setEntity(new ByteArrayEntity(data, ContentType.parse(entity.getContentType()))); } } } ././@LongLink0100644 0000000 0000000 00000000146 14403631147 011637 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicHttpCoreTransportTest.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicHttpCoreTransportTest.jav0100664 0000000 0000000 00000014477 14403631147 032734 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; public abstract class ClassicHttpCoreTransportTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); private final URIScheme scheme; public ClassicHttpCoreTransportTest(final URIScheme scheme) { this.scheme = scheme; } abstract HttpServer serverStart() throws IOException; abstract HttpRequester clientStart() throws IOException; @Test public void testSequentialRequests() throws Exception { final HttpServer server = serverStart(); final HttpRequester requester = clientStart(); final HttpHost target = new HttpHost(scheme.id, "localhost", server.getLocalPort()); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff"); request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) { assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = EntityUtils.toString(response1.getEntity()); assertThat(body1, CoreMatchers.equalTo("some stuff")); } final ClassicHttpRequest request2 = new BasicClassicHttpRequest(Method.POST, "/other-stuff"); request2.setEntity(new StringEntity("some other stuff", ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response2 = requester.execute(target, request2, TIMEOUT, context)) { assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = EntityUtils.toString(response2.getEntity()); assertThat(body2, CoreMatchers.equalTo("some other stuff")); } final ClassicHttpRequest request3 = new BasicClassicHttpRequest(Method.POST, "/more-stuff"); request3.setEntity(new StringEntity("some more stuff", ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response3 = requester.execute(target, request3, TIMEOUT, context)) { assertThat(response3.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body3 = EntityUtils.toString(response3.getEntity()); assertThat(body3, CoreMatchers.equalTo("some more stuff")); } } @Test public void testSequentialRequestsNonPersistentConnection() throws Exception { final HttpServer server = serverStart(); final HttpRequester requester = clientStart(); final HttpHost target = new HttpHost(scheme.id, "localhost", server.getLocalPort()); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/no-keep-alive/stuff"); request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) { assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = EntityUtils.toString(response1.getEntity()); assertThat(body1, CoreMatchers.equalTo("some stuff")); } final ClassicHttpRequest request2 = new BasicClassicHttpRequest(Method.POST, "/no-keep-alive/other-stuff"); request2.setEntity(new StringEntity("some other stuff", ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response2 = requester.execute(target, request2, TIMEOUT, context)) { assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = EntityUtils.toString(response2.getEntity()); assertThat(body2, CoreMatchers.equalTo("some other stuff")); } final ClassicHttpRequest request3 = new BasicClassicHttpRequest(Method.POST, "/no-keep-alive/more-stuff"); request3.setEntity(new StringEntity("some more stuff", ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response3 = requester.execute(target, request3, TIMEOUT, context)) { assertThat(response3.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body3 = EntityUtils.toString(response3.getEntity()); assertThat(body3, CoreMatchers.equalTo("some more stuff")); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicIntegrationTest.java0100664 0000000 0000000 00000076336 14435411677 031726 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Random; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.io.entity.AbstractHttpEntity; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.io.support.BasicHttpServerExpectationDecorator; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.RequestConnControl; import org.apache.hc.core5.http.protocol.RequestContent; import org.apache.hc.core5.http.protocol.RequestExpectContinue; import org.apache.hc.core5.http.protocol.RequestTargetHost; import org.apache.hc.core5.http.protocol.RequestUserAgent; import org.apache.hc.core5.testing.classic.extension.ClassicTestResources; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class ClassicIntegrationTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); private final URIScheme scheme; @RegisterExtension private final ClassicTestResources testResources; public ClassicIntegrationTest(final URIScheme scheme) { this.scheme = scheme; this.testResources = new ClassicTestResources(scheme, TIMEOUT); } /** * This test case executes a series of simple GET requests */ @Test public void testSimpleBasicHttpRequests() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); final int reqNo = 20; final Random rnd = new Random(); // Prepare some random data final List testData = new ArrayList<>(reqNo); for (int i = 0; i < reqNo; i++) { final int size = rnd.nextInt(5000); final byte[] data = new byte[size]; rnd.nextBytes(data); testData.add(data); } // Initialize the server-side request handler server.registerHandler("*", (request, response, context) -> { String s = request.getPath(); if (s.startsWith("/?")) { s = s.substring(2); } final int index = Integer.parseInt(s); final byte[] data = testData.get(index); final ByteArrayEntity entity = new ByteArrayEntity(data, null); response.setEntity(entity); }); server.start(); client.start(); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); for (int r = 0; r < reqNo; r++) { final BasicClassicHttpRequest get = new BasicClassicHttpRequest(Method.GET, "/?" + r); try (final ClassicHttpResponse response = client.execute(host, get, context)) { final byte[] received = EntityUtils.toByteArray(response.getEntity()); final byte[] expected = testData.get(r); Assertions.assertEquals(expected.length, received.length); for (int i = 0; i < expected.length; i++) { Assertions.assertEquals(expected[i], received[i]); } } } } /** * This test case executes a series of simple POST requests with content length * delimited content. */ @Test public void testSimpleHttpPostsWithContentLength() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); final int reqNo = 20; final Random rnd = new Random(); // Prepare some random data final List testData = new ArrayList<>(reqNo); for (int i = 0; i < reqNo; i++) { final int size = rnd.nextInt(5000); final byte[] data = new byte[size]; rnd.nextBytes(data); testData.add(data); } // Initialize the server-side request handler server.registerHandler("*", (request, response, context) -> { final HttpEntity entity = request.getEntity(); if (entity != null) { final byte[] data = EntityUtils.toByteArray(entity); response.setEntity(new ByteArrayEntity(data, null)); } }); server.start(); client.start(); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); for (int r = 0; r < reqNo; r++) { final BasicClassicHttpRequest post = new BasicClassicHttpRequest(Method.POST, "/"); final byte[] data = testData.get(r); post.setEntity(new ByteArrayEntity(data, null)); try (final ClassicHttpResponse response = client.execute(host, post, context)) { final byte[] received = EntityUtils.toByteArray(response.getEntity()); final byte[] expected = testData.get(r); Assertions.assertEquals(expected.length, received.length); for (int i = 0; i < expected.length; i++) { Assertions.assertEquals(expected[i], received[i]); } } } } /** * This test case executes a series of simple POST requests with chunk * coded content content. */ @Test public void testSimpleHttpPostsChunked() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); final int reqNo = 20; final Random rnd = new Random(); // Prepare some random data final List testData = new ArrayList<>(reqNo); for (int i = 0; i < reqNo; i++) { final int size = rnd.nextInt(20000); final byte[] data = new byte[size]; rnd.nextBytes(data); testData.add(data); } // Initialize the server-side request handler server.registerHandler("*", (request, response, context) -> { final HttpEntity entity = request.getEntity(); if (entity != null) { final byte[] data = EntityUtils.toByteArray(entity); response.setEntity(new ByteArrayEntity(data, null, true)); } }); server.start(); client.start(); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); for (int r = 0; r < reqNo; r++) { final BasicClassicHttpRequest post = new BasicClassicHttpRequest(Method.POST, "/"); final byte[] data = testData.get(r); post.setEntity(new ByteArrayEntity(data, null, true)); try (final ClassicHttpResponse response = client.execute(host, post, context)) { final byte[] received = EntityUtils.toByteArray(response.getEntity()); final byte[] expected = testData.get(r); Assertions.assertEquals(expected.length, received.length); for (int i = 0; i < expected.length; i++) { Assertions.assertEquals(expected[i], received[i]); } } } } /** * This test case executes a series of simple HTTP/1.0 POST requests. */ @Test public void testSimpleHttpPostsHTTP10() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); final int reqNo = 20; final Random rnd = new Random(); // Prepare some random data final List testData = new ArrayList<>(reqNo); for (int i = 0; i < reqNo; i++) { final int size = rnd.nextInt(5000); final byte[] data = new byte[size]; rnd.nextBytes(data); testData.add(data); } // Initialize the server-side request handler server.registerHandler("*", (request, response, context) -> { final HttpEntity entity = request.getEntity(); if (entity != null) { final byte[] data = EntityUtils.toByteArray(entity); response.setEntity(new ByteArrayEntity(data, null)); } if (HttpVersion.HTTP_1_0.equals(request.getVersion())) { response.addHeader("Version", "1.0"); } }); server.start(); client.start(); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); for (int r = 0; r < reqNo; r++) { // Set protocol level to HTTP/1.0 final BasicClassicHttpRequest post = new BasicClassicHttpRequest(Method.POST, "/"); post.setVersion(HttpVersion.HTTP_1_0); final byte[] data = testData.get(r); post.setEntity(new ByteArrayEntity(data, null)); try (final ClassicHttpResponse response = client.execute(host, post, context)) { Assertions.assertEquals(HttpVersion.HTTP_1_1, response.getVersion()); final Header h1 = response.getFirstHeader("Version"); Assertions.assertNotNull(h1); Assertions.assertEquals("1.0", h1.getValue()); final byte[] received = EntityUtils.toByteArray(response.getEntity()); final byte[] expected = testData.get(r); Assertions.assertEquals(expected.length, received.length); for (int i = 0; i < expected.length; i++) { Assertions.assertEquals(expected[i], received[i]); } } } } /** * This test case executes a series of simple POST requests using * the 'expect: continue' handshake. */ @Test public void testHttpPostsWithExpectContinue() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); final int reqNo = 20; final Random rnd = new Random(); // Prepare some random data final List testData = new ArrayList<>(reqNo); for (int i = 0; i < reqNo; i++) { final int size = rnd.nextInt(5000); final byte[] data = new byte[size]; rnd.nextBytes(data); testData.add(data); } // Initialize the server-side request handler server.registerHandler("*", (request, response, context) -> { final HttpEntity entity = request.getEntity(); if (entity != null) { final byte[] data = EntityUtils.toByteArray(entity); response.setEntity(new ByteArrayEntity(data, null, true)); } }); server.start(); client.start(); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); for (int r = 0; r < reqNo; r++) { final BasicClassicHttpRequest post = new BasicClassicHttpRequest(Method.POST, "/"); final byte[] data = testData.get(r); post.setEntity(new ByteArrayEntity(data, null, true)); try (final ClassicHttpResponse response = client.execute(host, post, context)) { final byte[] received = EntityUtils.toByteArray(response.getEntity()); final byte[] expected = testData.get(r); Assertions.assertEquals(expected.length, received.length); for (int i = 0; i < expected.length; i++) { Assertions.assertEquals(expected[i], received[i]); } } } } /** * This test case executes a series of simple POST requests that do not * meet the target server expectations. */ @Test public void testHttpPostsWithExpectationVerification() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); final int reqNo = 20; // Initialize the server-side request handler server.registerHandler("*", (request, response, context) -> response.setEntity(new StringEntity("No content"))); server.start(null, null, handler -> new BasicHttpServerExpectationDecorator(handler) { @Override protected ClassicHttpResponse verify(final ClassicHttpRequest request, final HttpContext context) { final Header someheader = request.getFirstHeader("Secret"); if (someheader != null) { final int secretNumber; try { secretNumber = Integer.parseInt(someheader.getValue()); } catch (final NumberFormatException ex) { final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_BAD_REQUEST); response.setEntity(new StringEntity(ex.toString())); return response; } if (secretNumber >= 2) { final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_EXPECTATION_FAILED); response.setEntity(new StringEntity("Wrong secret number", ContentType.TEXT_PLAIN)); return response; } } return null; } }); client.start(); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); for (int r = 0; r < reqNo; r++) { final BasicClassicHttpRequest post = new BasicClassicHttpRequest(Method.POST, "/"); post.addHeader("Secret", Integer.toString(r)); final byte[] b = new byte[2048]; for (int i = 0; i < b.length; i++) { b[i] = (byte) ('a' + r); } post.setEntity(new ByteArrayEntity(b, ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response = client.execute(host, post, context)) { final HttpEntity responseEntity = response.getEntity(); Assertions.assertNotNull(responseEntity); EntityUtils.consume(responseEntity); if (r >= 2) { Assertions.assertEquals(HttpStatus.SC_EXPECTATION_FAILED, response.getCode()); } else { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); } } } } static class RepeatingEntity extends AbstractHttpEntity { private final byte[] raw; private final int n; public RepeatingEntity(final String content, final Charset charset, final int n, final boolean chunked) { super(ContentType.TEXT_PLAIN.withCharset(charset), null, chunked); final Charset cs = charset != null ? charset : StandardCharsets.US_ASCII; this.raw = content.getBytes(cs); this.n = n; } @Override public InputStream getContent() throws IOException, IllegalStateException { throw new IllegalStateException("This method is not implemented"); } @Override public long getContentLength() { return (this.raw.length + 2) * this.n; } @Override public boolean isRepeatable() { return true; } @Override public boolean isStreaming() { return false; } @Override public void writeTo(final OutputStream outStream) throws IOException { for (int i = 0; i < this.n; i++) { outStream.write(this.raw); outStream.write('\r'); outStream.write('\n'); } outStream.flush(); } @Override public void close() throws IOException { } } @Test public void testHttpContent() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); final String[] patterns = { "0123456789ABCDEF", "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" + "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that" }; // Initialize the server-side request handler server.registerHandler("*", (request, response, context) -> { int n = 1; String s = request.getPath(); if (s.startsWith("/?n=")) { s = s.substring(4); try { n = Integer.parseInt(s); if (n <= 0) { throw new HttpException("Invalid request: " + "number of repetitions cannot be negative or zero"); } } catch (final NumberFormatException ex) { throw new HttpException("Invalid request: " + "number of repetitions is invalid"); } } final HttpEntity entity = request.getEntity(); if (entity != null) { final String line = EntityUtils.toString(entity); final ContentType contentType = ContentType.parse(entity.getContentType()); final Charset charset = ContentType.getCharset(contentType, StandardCharsets.ISO_8859_1); response.setEntity(new RepeatingEntity(line, charset, n, n % 2 == 0)); } }); server.start(); client.start(); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); for (final String pattern : patterns) { for (int n = 1000; n < 1020; n++) { final BasicClassicHttpRequest post = new BasicClassicHttpRequest( Method.POST.name(), "/?n=" + n); post.setEntity(new StringEntity(pattern, ContentType.TEXT_PLAIN, n % 2 == 0)); try (final ClassicHttpResponse response = client.execute(host, post, context)) { final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); final InputStream inStream = entity.getContent(); final ContentType contentType = ContentType.parse(entity.getContentType()); final Charset charset = ContentType.getCharset(contentType, StandardCharsets.ISO_8859_1); Assertions.assertNotNull(inStream); final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, charset)); String line; int count = 0; while ((line = reader.readLine()) != null) { Assertions.assertEquals(pattern, line); count++; } Assertions.assertEquals(n, count); } } } } @Test public void testHttpPostNoEntity() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); server.registerHandler("*", (request, response, context) -> { final HttpEntity entity = request.getEntity(); if (entity != null) { final byte[] data = EntityUtils.toByteArray(entity); response.setEntity(new ByteArrayEntity(data, null)); } }); server.start(); client.start(); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); final BasicClassicHttpRequest post = new BasicClassicHttpRequest(Method.POST, "/"); post.setEntity(null); try (final ClassicHttpResponse response = client.execute(host, post, context)) { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); final byte[] received = EntityUtils.toByteArray(response.getEntity()); Assertions.assertEquals(0, received.length); } } @Test public void testHttpPostNoContentLength() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); server.registerHandler("*", (request, response, context) -> { final HttpEntity entity = request.getEntity(); if (entity != null) { final byte[] data = EntityUtils.toByteArray(entity); response.setEntity(new ByteArrayEntity(data, null)); } }); server.start(); client.start(new DefaultHttpProcessor( RequestTargetHost.INSTANCE, RequestConnControl.INSTANCE, RequestUserAgent.INSTANCE, RequestExpectContinue.INSTANCE)); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); final BasicClassicHttpRequest post = new BasicClassicHttpRequest(Method.POST, "/"); post.setEntity(null); try (final ClassicHttpResponse response = client.execute(host, post, context)) { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); final byte[] received = EntityUtils.toByteArray(response.getEntity()); Assertions.assertEquals(0, received.length); } } @Test public void testHttpPostIdentity() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); server.registerHandler("*", (request, response, context) -> { final HttpEntity entity = request.getEntity(); if (entity != null) { final byte[] data = EntityUtils.toByteArray(entity); response.setEntity(new ByteArrayEntity(data, null)); } }); server.start(); client.start(new DefaultHttpProcessor( (request, entity, context) -> request.addHeader(HttpHeaders.TRANSFER_ENCODING, "identity"), RequestTargetHost.INSTANCE, RequestConnControl.INSTANCE, RequestUserAgent.INSTANCE, RequestExpectContinue.INSTANCE)); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); final BasicClassicHttpRequest post = new BasicClassicHttpRequest(Method.POST, "/"); post.setEntity(null); try (final ClassicHttpResponse response = client.execute(host, post, context)) { Assertions.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getCode()); } } @Test public void testNoContentResponse() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); final int reqNo = 20; // Initialize the server-side request handler server.registerHandler("*", (request, response, context) -> response.setCode(HttpStatus.SC_NO_CONTENT)); server.start(); client.start(); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); for (int r = 0; r < reqNo; r++) { final BasicClassicHttpRequest get = new BasicClassicHttpRequest(Method.GET, "/?" + r); try (final ClassicHttpResponse response = client.execute(host, get, context)) { Assertions.assertNull(response.getEntity()); } } } @Test public void testAbsentHostHeader() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); // Initialize the server-side request handler server.registerHandler("*", (request, response, context) -> response.setEntity(new StringEntity("All is well", StandardCharsets.US_ASCII))); server.start(); client.start(new DefaultHttpProcessor(RequestContent.INSTANCE, new RequestConnControl())); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); final BasicClassicHttpRequest get1 = new BasicClassicHttpRequest(Method.GET, "/"); get1.setVersion(HttpVersion.HTTP_1_0); try (final ClassicHttpResponse response1 = client.execute(host, get1, context)) { Assertions.assertEquals(200, response1.getCode()); EntityUtils.consume(response1.getEntity()); } final BasicClassicHttpRequest get2 = new BasicClassicHttpRequest(Method.GET, "/"); try (final ClassicHttpResponse response2 = client.execute(host, get2, context)) { Assertions.assertEquals(400, response2.getCode()); EntityUtils.consume(response2.getEntity()); } } @Test public void testHeaderTooLarge() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); server.registerHandler("*", (request, response, context) -> response.setEntity(new StringEntity("All is well", StandardCharsets.US_ASCII))); server.start( Http1Config.custom() .setMaxLineLength(100) .build(), null, null); client.start(); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); final BasicClassicHttpRequest get1 = new BasicClassicHttpRequest(Method.GET, "/"); get1.setHeader("big-f-header", "1234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890"); try (final ClassicHttpResponse response1 = client.execute(host, get1, context)) { Assertions.assertEquals(431, response1.getCode()); EntityUtils.consume(response1.getEntity()); } } @Test public void testHeaderTooLargePost() throws Exception { final ClassicTestServer server = testResources.server(); final ClassicTestClient client = testResources.client(); server.registerHandler("*", (request, response, context) -> response.setEntity(new StringEntity("All is well", StandardCharsets.US_ASCII))); server.start( Http1Config.custom() .setMaxLineLength(100) .build(), null, null); client.start( new DefaultHttpProcessor(RequestContent.INSTANCE, RequestTargetHost.INSTANCE, RequestConnControl.INSTANCE)); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getPort()); final ClassicHttpRequest post1 = new BasicClassicHttpRequest(Method.POST, "/"); post1.setHeader("big-f-header", "1234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890"); final byte[] b = new byte[2048]; for (int i = 0; i < b.length; i++) { b[i] = (byte) ('a' + i % 10); } post1.setEntity(new ByteArrayEntity(b, ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response1 = client.execute(host, post1, context)) { Assertions.assertEquals(431, response1.getCode()); EntityUtils.consume(response1.getEntity()); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicProtocolTests.java0100664 0000000 0000000 00000003366 14403631147 031407 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import org.apache.hc.core5.http.URIScheme; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; public class ClassicProtocolTests { @Nested @DisplayName("Classic HTTP/1.1 (plain)") public class Http1 extends ClassicIntegrationTest { public Http1() { super(URIScheme.HTTP); } } @Nested @DisplayName("Classic HTTP/1.1 (TLS)") public class Http1Tls extends ClassicIntegrationTest { public Http1Tls() { super(URIScheme.HTTPS); } } } ././@LongLink0100644 0000000 0000000 00000000147 14403631147 011640 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicHttp1CoreTransportTest.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicHttp1CoreTransportTest.ja0100664 0000000 0000000 00000010232 14403631147 032610 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.StandardFilter; import org.apache.hc.core5.http.io.HttpFilterChain; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.testing.classic.extension.HttpRequesterResource; import org.apache.hc.core5.testing.classic.extension.HttpServerResource; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class ClassicHttp1CoreTransportTest extends ClassicHttpCoreTransportTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private final HttpServerResource serverResource; @RegisterExtension private final HttpRequesterResource clientResource; public ClassicHttp1CoreTransportTest(final URIScheme scheme) { super(scheme); this.serverResource = new HttpServerResource(scheme, bootstrap -> bootstrap .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .register("*", new EchoHandler()) .addFilterBefore(StandardFilter.MAIN_HANDLER.name(), "no-keep-alive", (request, responseTrigger, context, chain) -> chain.proceed(request, new HttpFilterChain.ResponseTrigger() { @Override public void sendInformation( final ClassicHttpResponse response) throws HttpException, IOException { responseTrigger.sendInformation(response); } @Override public void submitResponse( final ClassicHttpResponse response) throws HttpException, IOException { if (request.getPath().startsWith("/no-keep-alive")) { response.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); } responseTrigger.submitResponse(response); } }, context))); this.clientResource = new HttpRequesterResource(bootstrap -> bootstrap .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build())); } @Override HttpServer serverStart() throws IOException { return serverResource.start(); } @Override HttpRequester clientStart() throws IOException { return clientResource.start(); } } ././@LongLink0100644 0000000 0000000 00000000175 14403631147 011641 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/MonitoringResponseOutOfOrderStrategyIntegrationTest.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/MonitoringResponseOutOfOrderStra0100664 0000000 0000000 00000014046 14403631147 033005 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnectionFactory; import org.apache.hc.core5.http.impl.io.MonitoringResponseOutOfOrderStrategy; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.AbstractHttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.testing.classic.extension.HttpRequesterResource; import org.apache.hc.core5.testing.classic.extension.HttpServerResource; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class MonitoringResponseOutOfOrderStrategyIntegrationTest { // Use a 16k buffer for consistent results across systems private static final int BUFFER_SIZE = 16 * 1024; private static final Timeout TIMEOUT = Timeout.ofSeconds(3); private final URIScheme scheme; @RegisterExtension private final HttpServerResource serverResource; @RegisterExtension private final HttpRequesterResource clientResource; public MonitoringResponseOutOfOrderStrategyIntegrationTest(final URIScheme scheme) { this.scheme = scheme; this.serverResource = new HttpServerResource(scheme, bootstrap -> bootstrap .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .setSndBufSize(BUFFER_SIZE) .setRcvBufSize(BUFFER_SIZE) .setSoKeepAlive(false) .build()) .register("*", (request, response, context) -> { response.setCode(400); response.setEntity(new AllOnesHttpEntity(200000)); })); this.clientResource = new HttpRequesterResource(bootstrap -> bootstrap .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .setRcvBufSize(BUFFER_SIZE) .setSndBufSize(BUFFER_SIZE) .setSoKeepAlive(false) .build()) .setConnectionFactory(DefaultBHttpClientConnectionFactory.builder() .responseOutOfOrderStrategy(MonitoringResponseOutOfOrderStrategy.INSTANCE) .build())); } @Test @org.junit.jupiter.api.Timeout(value = 1, unit = TimeUnit.MINUTES)// Failures may hang public void testResponseOutOfOrderWithDefaultStrategy() throws Exception { final HttpServer server = serverResource.start(); final HttpRequester requester = clientResource.start(); final HttpCoreContext context = HttpCoreContext.create(); final HttpHost host = new HttpHost(scheme.id, "localhost", server.getLocalPort()); final ClassicHttpRequest post = new BasicClassicHttpRequest(Method.POST, "/"); post.setEntity(new AllOnesHttpEntity(200000)); try (final ClassicHttpResponse response = requester.execute(host, post, TIMEOUT, context)) { Assertions.assertEquals(400, response.getCode()); EntityUtils.consumeQuietly(response.getEntity()); } } private static final class AllOnesHttpEntity extends AbstractHttpEntity { private long remaining; protected AllOnesHttpEntity(final long length) { super(ContentType.APPLICATION_OCTET_STREAM, null, true); this.remaining = length; } @Override public InputStream getContent() { throw new UnsupportedOperationException(); } @Override public void writeTo(final OutputStream outStream) throws IOException { final byte[] buf = new byte[1024]; while (remaining > 0) { final int writeLength = (int) Math.min(remaining, buf.length); outStream.write(buf, 0, writeLength); outStream.flush(); remaining -= writeLength; } } @Override public boolean isStreaming() { return true; } @Override public void close() { } @Override public long getContentLength() { return -1L; } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/extension/0040775 0000000 0000000 00000000000 14403631147 026425 5ustar000000000 0000000 ././@LongLink0100644 0000000 0000000 00000000146 14403631147 011637 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/extension/HttpServerResource.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/extension/HttpServerResource.jav0100664 0000000 0000000 00000006705 14403631147 032752 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic.extension; import java.io.IOException; import java.util.function.Consumer; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.classic.LoggingExceptionListener; import org.apache.hc.core5.testing.classic.LoggingHttp1StreamListener; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HttpServerResource implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(HttpServerResource.class); private final URIScheme scheme; private final Consumer bootstrapCustomizer; private HttpServer server; public HttpServerResource(final URIScheme scheme, final Consumer bootstrapCustomizer) { this.scheme = scheme; this.bootstrapCustomizer = bootstrapCustomizer; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test server"); final ServerBootstrap bootstrap = ServerBootstrap.bootstrap() .setSslContext(scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null) .setExceptionListener(LoggingExceptionListener.INSTANCE) .setStreamListener(LoggingHttp1StreamListener.INSTANCE); bootstrapCustomizer.accept(bootstrap); server = bootstrap.create(); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test server"); if (server != null) { try { server.close(CloseMode.IMMEDIATE); } catch (final Exception ignore) { } } } public HttpServer start() throws IOException { Assertions.assertNotNull(server); server.start(); return server; } } ././@LongLink0100644 0000000 0000000 00000000150 14403631147 011632 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/extension/ClassicTestResources.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/extension/ClassicTestResources.j0100664 0000000 0000000 00000007157 14403631147 032723 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic.extension; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.classic.ClassicTestClient; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ClassicTestResources implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(ClassicTestResources.class); private final URIScheme scheme; private final Timeout socketTimeout; private ClassicTestServer server; private ClassicTestClient client; public ClassicTestResources(final URIScheme scheme, final Timeout socketTimeout) { this.scheme = scheme; this.socketTimeout = socketTimeout; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test server"); server = new ClassicTestServer( scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null, SocketConfig.custom() .setSoTimeout(socketTimeout) .build()); LOG.debug("Starting up test client"); client = new ClassicTestClient( scheme == URIScheme.HTTPS ? SSLTestContexts.createClientSSLContext() : null, SocketConfig.custom() .setSoTimeout(socketTimeout) .build()); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test client"); if (client != null) { client.shutdown(CloseMode.IMMEDIATE); } LOG.debug("Shutting down test server"); if (server != null) { server.shutdown(CloseMode.IMMEDIATE); } } public ClassicTestClient client() { Assertions.assertNotNull(client); return client; } public ClassicTestServer server() { Assertions.assertNotNull(server); return server; } } ././@LongLink0100644 0000000 0000000 00000000151 14403631147 011633 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/extension/HttpRequesterResource.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/extension/HttpRequesterResource.0100664 0000000 0000000 00000006516 14403631147 032762 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic.extension; import java.util.function.Consumer; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.bootstrap.RequesterBootstrap; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.classic.LoggingConnPoolListener; import org.apache.hc.core5.testing.classic.LoggingHttp1StreamListener; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HttpRequesterResource implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(HttpRequesterResource.class); private final Consumer bootstrapCustomizer; private HttpRequester requester; public HttpRequesterResource(final Consumer bootstrapCustomizer) { this.bootstrapCustomizer = bootstrapCustomizer; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test client"); final RequesterBootstrap bootstrap = RequesterBootstrap.bootstrap() .setSslContext(SSLTestContexts.createClientSSLContext()) .setMaxTotal(2) .setDefaultMaxPerRoute(2) .setStreamListener(LoggingHttp1StreamListener.INSTANCE) .setConnPoolListener(LoggingConnPoolListener.INSTANCE); bootstrapCustomizer.accept(bootstrap); requester = bootstrap.create(); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test client"); if (requester != null) { try { requester.close(CloseMode.GRACEFUL); } catch (final Exception ignore) { } } } public HttpRequester start() { Assertions.assertNotNull(requester); return requester; } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicAuthenticationTest.java0100664 0000000 0000000 00000023665 14403631147 032406 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.charset.StandardCharsets; import java.util.Random; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.StandardFilter; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.io.support.AbstractHttpServerAuthFilter; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.testing.classic.extension.HttpRequesterResource; import org.apache.hc.core5.testing.classic.extension.HttpServerResource; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class ClassicAuthenticationTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private HttpServerResource serverResource; @RegisterExtension private HttpRequesterResource clientResource; public ClassicAuthenticationTest(final Boolean respondImmediately) { this.serverResource = new HttpServerResource(URIScheme.HTTP, bootstrap -> bootstrap .setSocketConfig( SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .register("*", new EchoHandler()) .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), new AbstractHttpServerAuthFilter(respondImmediately) { @Override protected String parseChallengeResponse( final String challenge, final HttpContext context) throws HttpException { return challenge; } @Override protected boolean authenticate( final String challengeResponse, final URIAuthority authority, final String requestUri, final HttpContext context) { return challengeResponse != null && challengeResponse.equals("let me pass"); } @Override protected String generateChallenge( final String challengeResponse, final URIAuthority authority, final String requestUri, final HttpContext context) { return "who goes there?"; } @Override protected HttpEntity generateResponseContent(final HttpResponse unauthorized) { return new StringEntity("You shall not pass!!!"); } }) ); this.clientResource = new HttpRequesterResource(bootstrap -> bootstrap .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) ); } @Test public void testGetRequestAuthentication() throws Exception { final HttpServer server = serverResource.start(); final HttpRequester requester = clientResource.start(); final HttpHost target = new HttpHost("localhost", server.getLocalPort()); final HttpCoreContext context = HttpCoreContext.create(); final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.GET, "/stuff"); try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) { assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED)); final String body1 = EntityUtils.toString(response1.getEntity()); assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!")); } final ClassicHttpRequest request2 = new BasicClassicHttpRequest(Method.GET, "/stuff"); request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass"); try (final ClassicHttpResponse response2 = requester.execute(target, request2, TIMEOUT, context)) { assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = EntityUtils.toString(response2.getEntity()); assertThat(body1, CoreMatchers.equalTo("")); } } @Test public void testPostRequestAuthentication() throws Exception { final HttpServer server = serverResource.start(); final HttpRequester requester = clientResource.start(); final HttpHost target = new HttpHost("localhost", server.getLocalPort()); final HttpCoreContext context = HttpCoreContext.create(); final Random rnd = new Random(); final byte[] stuff = new byte[10240]; for (int i = 0; i < stuff.length; i++) { stuff[i] = (byte) ('a' + rnd.nextInt(10)); } final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff"); request1.setEntity(new ByteArrayEntity(stuff, ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) { assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED)); final String body1 = EntityUtils.toString(response1.getEntity()); assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!")); } final ClassicHttpRequest request2 = new BasicClassicHttpRequest(Method.POST, "/stuff"); request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass"); request2.setEntity(new ByteArrayEntity(stuff, ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response2 = requester.execute(target, request2, TIMEOUT, context)) { assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = EntityUtils.toString(response2.getEntity()); assertThat(body1, CoreMatchers.equalTo(new String(stuff, StandardCharsets.US_ASCII))); } } @Test public void testPostRequestAuthenticationNoExpectContinue() throws Exception { final HttpServer server = serverResource.start(); final HttpRequester requester = clientResource.start(); final HttpHost target = new HttpHost("localhost", server.getLocalPort()); final HttpCoreContext context = HttpCoreContext.create(); final Random rnd = new Random(); final byte[] stuff = new byte[10240]; for (int i = 0; i < stuff.length; i++) { stuff[i] = (byte) ('a' + rnd.nextInt(10)); } final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff"); request1.setVersion(HttpVersion.HTTP_1_0); request1.setEntity(new ByteArrayEntity(stuff, ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) { assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED)); final String body1 = EntityUtils.toString(response1.getEntity()); assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!")); } final ClassicHttpRequest request2 = new BasicClassicHttpRequest(Method.POST, "/stuff"); request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass"); request2.setVersion(HttpVersion.HTTP_1_0); request2.setEntity(new ByteArrayEntity(stuff, ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response2 = requester.execute(target, request2, TIMEOUT, context)) { assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = EntityUtils.toString(response2.getEntity()); assertThat(body1, CoreMatchers.equalTo(new String(stuff, StandardCharsets.US_ASCII))); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicIntegrationTests.java0100664 0000000 0000000 00000006746 14403631147 032076 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import org.apache.hc.core5.http.URIScheme; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; public class ClassicIntegrationTests { @Nested @DisplayName("Core transport") public class CoreTransport extends ClassicHttp1CoreTransportTest { public CoreTransport() { super(URIScheme.HTTP); } } @Nested @DisplayName("Core transport (TLS)") public class CoreTransportTls extends ClassicHttp1CoreTransportTest { public CoreTransportTls() { super(URIScheme.HTTPS); } } @Nested @DisplayName("Authentication") public class Authentication extends ClassicAuthenticationTest { public Authentication() { super(false); } } @Nested @DisplayName("Authentication (immediate response)") public class AuthenticationImmediateResponse extends ClassicAuthenticationTest { public AuthenticationImmediateResponse() { super(true); } } @Nested @DisplayName("Out-of-order response monitoring") public class MonitoringResponseOutOfOrderStrategy extends MonitoringResponseOutOfOrderStrategyIntegrationTest { public MonitoringResponseOutOfOrderStrategy() { super(URIScheme.HTTP); } } @Nested @DisplayName("Out-of-order response monitoring (TLS)") public class MonitoringResponseOutOfOrderStrategyTls extends MonitoringResponseOutOfOrderStrategyIntegrationTest { public MonitoringResponseOutOfOrderStrategyTls() { super(URIScheme.HTTPS); } } @Nested @DisplayName("Server filters") public class HttpFilters extends ClassicServerBootstrapFilterTest { public HttpFilters() { super(URIScheme.HTTP); } } @Nested @DisplayName("Core transport (SOCKS)") public class CoreTransportSocksProxy extends ClassicHttp1SocksProxyCoreTransportTest { public CoreTransportSocksProxy() { super(URIScheme.HTTP); } } @Nested @DisplayName("Core transport (TLS, SOCKS)") public class CoreTransportSocksProxyTls extends ClassicHttp1SocksProxyCoreTransportTest { public CoreTransportSocksProxyTls() { super(URIScheme.HTTPS); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicTLSIntegrationTest.java0100664 0000000 0000000 00000031731 14403631147 032266 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.classic; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLSession; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.impl.bootstrap.HttpRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.RequesterBootstrap; import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.RegisterExtension; public class ClassicTLSIntegrationTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); private HttpServer server; @RegisterExtension public final AfterEachCallback serverCleanup = new AfterEachCallback() { @Override public void afterEach(final ExtensionContext context) throws Exception { if (server != null) { try { server.close(CloseMode.IMMEDIATE); } catch (final Exception ignore) { } } } }; private HttpRequester requester; @RegisterExtension public final AfterEachCallback clientCleanup = new AfterEachCallback() { @Override public void afterEach(final ExtensionContext context) throws Exception { if (requester != null) { try { requester.close(CloseMode.GRACEFUL); } catch (final Exception ignore) { } } } }; @Test public void testTLSSuccess() throws Exception { server = ServerBootstrap.bootstrap() .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setSslContext(SSLTestContexts.createServerSSLContext()) .setExceptionListener(LoggingExceptionListener.INSTANCE) .setStreamListener(LoggingHttp1StreamListener.INSTANCE) .register("*", new EchoHandler()) .create(); server.start(); final AtomicReference sslSessionRef = new AtomicReference<>(); requester = RequesterBootstrap.bootstrap() .setSslContext(SSLTestContexts.createClientSSLContext()) .setSslSessionVerifier((endpoint, sslSession) -> sslSessionRef.set(sslSession)) .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setStreamListener(LoggingHttp1StreamListener.INSTANCE) .setConnPoolListener(LoggingConnPoolListener.INSTANCE) .create(); final HttpContext context = new BasicHttpContext(); final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort()); final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff"); request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) { assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = EntityUtils.toString(response1.getEntity()); assertThat(body1, CoreMatchers.equalTo("some stuff")); } final SSLSession sslSession = sslSessionRef.getAndSet(null); final ProtocolVersion tlsVersion = TLS.parse(sslSession.getProtocol()); assertThat(tlsVersion.greaterEquals(TLS.V_1_2.getVersion()), CoreMatchers.equalTo(true)); assertThat(sslSession.getPeerPrincipal().getName(), CoreMatchers.equalTo("CN=localhost,OU=Apache HttpComponents,O=Apache Software Foundation")); } @Test public void testTLSTrustFailure() throws Exception { server = ServerBootstrap.bootstrap() .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setSslContext(SSLTestContexts.createServerSSLContext()) .setExceptionListener(LoggingExceptionListener.INSTANCE) .setStreamListener(LoggingHttp1StreamListener.INSTANCE) .register("*", new EchoHandler()) .create(); server.start(); requester = RequesterBootstrap.bootstrap() .setSslContext(SSLContexts.createDefault()) .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setStreamListener(LoggingHttp1StreamListener.INSTANCE) .setConnPoolListener(LoggingConnPoolListener.INSTANCE) .create(); final HttpContext context = new BasicHttpContext(); final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort()); final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff"); request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN)); Assertions.assertThrows(IOException.class, () -> { try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) { EntityUtils.consume(response1.getEntity()); } }); } @Test public void testTLSClientAuthFailure() throws Exception { server = ServerBootstrap.bootstrap() .setSslContext(SSLTestContexts.createClientSSLContext()) .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setSslContext(SSLTestContexts.createServerSSLContext()) .setSslSetupHandler(sslParameters -> sslParameters.setNeedClientAuth(true)) .setExceptionListener(LoggingExceptionListener.INSTANCE) .setStreamListener(LoggingHttp1StreamListener.INSTANCE) .register("*", new EchoHandler()) .create(); server.start(); requester = RequesterBootstrap.bootstrap() .setSslContext(SSLTestContexts.createClientSSLContext()) .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setStreamListener(LoggingHttp1StreamListener.INSTANCE) .setConnPoolListener(LoggingConnPoolListener.INSTANCE) .create(); final HttpContext context = new BasicHttpContext(); final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort()); final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff"); request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN)); Assertions.assertThrows(IOException.class, () -> { try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) { EntityUtils.consume(response1.getEntity()); } }); } @Test public void testSSLDisabledByDefault() throws Exception { server = ServerBootstrap.bootstrap() .setSslContext(SSLTestContexts.createServerSSLContext()) .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[]{"SSLv3"})) .create(); server.start(); requester = RequesterBootstrap.bootstrap() .setSslContext(SSLTestContexts.createClientSSLContext()) .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setStreamListener(LoggingHttp1StreamListener.INSTANCE) .setConnPoolListener(LoggingConnPoolListener.INSTANCE) .create(); final HttpContext context = new BasicHttpContext(); final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort()); final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff"); request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN)); Assertions.assertThrows(IOException.class, () -> { try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) { EntityUtils.consume(response1.getEntity()); } }); } @Test public void testWeakCiphersDisabledByDefault() throws Exception { requester = RequesterBootstrap.bootstrap() .setSslContext(SSLTestContexts.createClientSSLContext()) .setSocketConfig(SocketConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setStreamListener(LoggingHttp1StreamListener.INSTANCE) .setConnPoolListener(LoggingConnPoolListener.INSTANCE) .create(); final String[] weakCiphersSuites = { "SSL_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_128_CBC_SHA", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_RSA_WITH_NULL_SHA", "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_256_GCM_SHA384", "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_NULL_SHA256", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5" }; for (final String cipherSuite : weakCiphersSuites) { server = ServerBootstrap.bootstrap() .setSslContext(SSLTestContexts.createServerSSLContext()) .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[]{cipherSuite})) .create(); Assertions.assertThrows(Exception.class, () -> { try { server.start(); final HttpContext context = new BasicHttpContext(); final HttpHost target = new HttpHost("https", "localhost", server.getLocalPort()); final ClassicHttpRequest request1 = new BasicClassicHttpRequest(Method.POST, "/stuff"); request1.setEntity(new StringEntity("some stuff", ContentType.TEXT_PLAIN)); try (final ClassicHttpResponse response1 = requester.execute(target, request1, TIMEOUT, context)) { EntityUtils.consume(response1.getEntity()); } } finally { server.close(CloseMode.IMMEDIATE); } }); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/compatibility/0040775 0000000 0000000 00000000000 14245617503 025645 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/compatibility/http2/0040775 0000000 0000000 00000000000 14403631147 026702 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/compatibility/http2/HttpBinIT.java0100664 0000000 0000000 00000003560 14403631147 031353 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.compatibility.http2; import org.apache.hc.core5.http.HttpHost; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class HttpBinIT { private H2CompatibilityTest h2CompatibilityTest; @BeforeEach public void start() throws Exception { h2CompatibilityTest = new H2CompatibilityTest(); h2CompatibilityTest.start(); } @AfterEach public void shutdown() throws Exception { h2CompatibilityTest.shutdown(); } @Test public void executeHttpBin() throws Exception { h2CompatibilityTest.executeHttpBin(new HttpHost("http", "localhost", 8082)); } } ././@LongLink0100644 0000000 0000000 00000000151 14403631147 011633 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/compatibility/http2/H2CompatibilityTest.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/compatibility/http2/H2CompatibilityTest.0100664 0000000 0000000 00000054724 14403631147 032557 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.compatibility.http2; import java.io.IOException; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.entity.DiscardingEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.AbstractAsyncPushHandler; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.testing.classic.LoggingConnPoolListener; import org.apache.hc.core5.testing.nio.LoggingExceptionCallback; import org.apache.hc.core5.testing.nio.LoggingH2StreamListener; import org.apache.hc.core5.testing.nio.LoggingHttp1StreamListener; import org.apache.hc.core5.testing.nio.LoggingIOSessionDecorator; import org.apache.hc.core5.testing.nio.LoggingIOSessionListener; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Timeout; public class H2CompatibilityTest { private final HttpAsyncRequester client; public static void main(final String... args) throws Exception { final HttpHost[] h2servers = new HttpHost[]{ new HttpHost("http", "localhost", 8080), new HttpHost("http", "localhost", 8081) }; final HttpHost httpbin = new HttpHost("http", "localhost", 8082); final H2CompatibilityTest test = new H2CompatibilityTest(); try { test.start(); for (final HttpHost h2server : h2servers) { test.executeH2(h2server); } test.executeHttpBin(httpbin); } finally { test.shutdown(); } } H2CompatibilityTest() throws Exception { this.client = H2RequesterBootstrap.bootstrap() .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setH2Config(H2Config.custom() .setPushEnabled(true) .build()) .setStreamListener(LoggingHttp1StreamListener.INSTANCE_CLIENT) .setStreamListener(LoggingH2StreamListener.INSTANCE) .setConnPoolListener(LoggingConnPoolListener.INSTANCE) .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE) .setExceptionCallback(LoggingExceptionCallback.INSTANCE) .setIOSessionListener(LoggingIOSessionListener.INSTANCE) .create(); } void start() throws Exception { client.start(); } void shutdown() throws Exception { client.close(CloseMode.GRACEFUL); } private static final Timeout TIMEOUT = Timeout.ofSeconds(5); void executeH2(final HttpHost target) throws Exception { { System.out.println("*** HTTP/2 simple request execution ***"); final Future connectFuture = client.connect(target, TIMEOUT, HttpVersionPolicy.FORCE_HTTP_2, null); try { final AsyncClientEndpoint endpoint = connectFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final CountDownLatch countDownLatch = new CountDownLatch(1); final HttpRequest httpget = new BasicHttpRequest(Method.GET, target, "/status.html"); endpoint.execute( new BasicRequestProducer(httpget, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), new FutureCallback>() { @Override public void completed(final Message responseMessage) { final HttpResponse response = responseMessage.getHead(); final int code = response.getCode(); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, target, httpget, response, Objects.toString(response.getFirstHeader("server"))); } else { logResult(TestResult.NOK, target, httpget, response, "(status " + code + ")"); } countDownLatch.countDown(); } @Override public void failed(final Exception ex) { logResult(TestResult.NOK, target, httpget, null, "(" + ex.getMessage() + ")"); countDownLatch.countDown(); } @Override public void cancelled() { logResult(TestResult.NOK, target, httpget, null, "(cancelled)"); countDownLatch.countDown(); } }); if (!countDownLatch.await(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())) { logResult(TestResult.NOK, target, null, null, "(single request execution failed to complete in time)"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, target, null, null, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, target, null, null, "(time out)"); } } { System.out.println("*** HTTP/2 multiplexed request execution ***"); final Future connectFuture = client.connect(target, TIMEOUT, HttpVersionPolicy.FORCE_HTTP_2, null); try { final AsyncClientEndpoint endpoint = connectFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int reqCount = 20; final CountDownLatch countDownLatch = new CountDownLatch(reqCount); for (int i = 0; i < reqCount; i++) { final HttpRequest httpget = new BasicHttpRequest(Method.GET, target, "/status.html"); endpoint.execute( new BasicRequestProducer(httpget, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), new FutureCallback>() { @Override public void completed(final Message responseMessage) { final HttpResponse response = responseMessage.getHead(); final int code = response.getCode(); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, target, httpget, response, "multiplexed / " + response.getFirstHeader("server")); } else { logResult(TestResult.NOK, target, httpget, response, "(status " + code + ")"); } countDownLatch.countDown(); } @Override public void failed(final Exception ex) { logResult(TestResult.NOK, target, httpget, null, "(" + ex.getMessage() + ")"); countDownLatch.countDown(); } @Override public void cancelled() { logResult(TestResult.NOK, target, httpget, null, "(cancelled)"); countDownLatch.countDown(); } }); } if (!countDownLatch.await(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())) { logResult(TestResult.NOK, target, null, null, "(multiplexed request execution failed to complete in time)"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, target, null, null, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, target, null, null, "(time out)"); } } { System.out.println("*** HTTP/2 request execution with push ***"); final Future connectFuture = client.connect(target, TIMEOUT, HttpVersionPolicy.FORCE_HTTP_2, null); try { final AsyncClientEndpoint endpoint = connectFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final CountDownLatch countDownLatch = new CountDownLatch(5); final HttpRequest httpget = new BasicHttpRequest(Method.GET, target, "/index.html"); final Future> future = endpoint.execute( new BasicRequestProducer(httpget, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), (request, context) -> new AbstractAsyncPushHandler>( new BasicResponseConsumer<>(new DiscardingEntityConsumer<>())) { @Override protected void handleResponse( final HttpRequest promise, final Message responseMessage) throws IOException, HttpException { final HttpResponse response = responseMessage.getHead(); logResult(TestResult.OK, target, promise, response, "pushed / " + response.getFirstHeader("server")); countDownLatch.countDown(); } @Override protected void handleError( final HttpRequest promise, final Exception cause) { logResult(TestResult.NOK, target, promise, null, "(" + cause.getMessage() + ")"); countDownLatch.countDown(); } }, null, null); final Message message = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final HttpResponse response = message.getHead(); final int code = response.getCode(); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, target, httpget, response, Objects.toString(response.getFirstHeader("server"))); if (!countDownLatch.await(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())) { logResult(TestResult.NOK, target, null, null, "Push messages not received"); } } else { logResult(TestResult.NOK, target, httpget, response, "(status " + code + ")"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, target, null, null, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, target, null, null, "(time out)"); } } } void executeHttpBin(final HttpHost target) throws Exception { { System.out.println("*** httpbin.org HTTP/1.1 simple request execution ***"); final List> requestMessages = Arrays.asList( new Message<>( new BasicHttpRequest(Method.GET, target, "/headers"), null), new Message<>( new BasicHttpRequest(Method.POST, target, "/anything"), new StringAsyncEntityProducer("some important message", ContentType.TEXT_PLAIN)), new Message<>( new BasicHttpRequest(Method.PUT, target, "/anything"), new StringAsyncEntityProducer("some important message", ContentType.TEXT_PLAIN)), new Message<>( new BasicHttpRequest(Method.GET, target, "/drip"), null), new Message<>( new BasicHttpRequest(Method.GET, target, "/bytes/20000"), null), new Message<>( new BasicHttpRequest(Method.GET, target, "/delay/2"), null), new Message<>( new BasicHttpRequest(Method.POST, target, "/delay/2"), new StringAsyncEntityProducer("some important message", ContentType.TEXT_PLAIN)), new Message<>( new BasicHttpRequest(Method.PUT, target, "/delay/2"), new StringAsyncEntityProducer("some important message", ContentType.TEXT_PLAIN)) ); for (final Message message : requestMessages) { final CountDownLatch countDownLatch = new CountDownLatch(1); final HttpRequest request = message.getHead(); final AsyncEntityProducer entityProducer = message.getBody(); client.execute( new BasicRequestProducer(request, entityProducer), new BasicResponseConsumer<>(new StringAsyncEntityConsumer(CharCodingConfig.custom() .setCharset(StandardCharsets.US_ASCII) .setMalformedInputAction(CodingErrorAction.IGNORE) .setUnmappableInputAction(CodingErrorAction.REPLACE) .build())), TIMEOUT, new FutureCallback>() { @Override public void completed(final Message responseMessage) { final HttpResponse response = responseMessage.getHead(); final int code = response.getCode(); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, target, request, response, Objects.toString(response.getFirstHeader("server"))); } else { logResult(TestResult.NOK, target, request, response, "(status " + code + ")"); } countDownLatch.countDown(); } @Override public void failed(final Exception ex) { logResult(TestResult.NOK, target, request, null, "(" + ex.getMessage() + ")"); countDownLatch.countDown(); } @Override public void cancelled() { logResult(TestResult.NOK, target, request, null, "(cancelled)"); countDownLatch.countDown(); } }); if (!countDownLatch.await(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())) { logResult(TestResult.NOK, target, null, null, "(httpbin.org tests failed to complete in time)"); } } } { System.out.println("*** httpbin.org HTTP/1.1 pipelined request execution ***"); final Future connectFuture = client.connect(target, TIMEOUT); final AsyncClientEndpoint streamEndpoint = connectFuture.get(); final int n = 10; final CountDownLatch countDownLatch = new CountDownLatch(n); for (int i = 0; i < n; i++) { final HttpRequest request; final AsyncEntityProducer entityProducer; if (i % 2 == 0) { request = new BasicHttpRequest(Method.GET, target, "/headers"); entityProducer = null; } else { request = new BasicHttpRequest(Method.POST, target, "/anything"); entityProducer = new StringAsyncEntityProducer("some important message", ContentType.TEXT_PLAIN); } streamEndpoint.execute( new BasicRequestProducer(request, entityProducer), new BasicResponseConsumer<>(new StringAsyncEntityConsumer(CharCodingConfig.custom() .setCharset(StandardCharsets.US_ASCII) .setMalformedInputAction(CodingErrorAction.IGNORE) .setUnmappableInputAction(CodingErrorAction.REPLACE) .build())), new FutureCallback>() { @Override public void completed(final Message responseMessage) { final HttpResponse response = responseMessage.getHead(); final int code = response.getCode(); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, target, request, response, "pipelined / " + response.getFirstHeader("server")); } else { logResult(TestResult.NOK, target, request, response, "(status " + code + ")"); } countDownLatch.countDown(); } @Override public void failed(final Exception ex) { logResult(TestResult.NOK, target, request, null, "(" + ex.getMessage() + ")"); countDownLatch.countDown(); } @Override public void cancelled() { logResult(TestResult.NOK, target, request, null, "(cancelled)"); countDownLatch.countDown(); } }); } if (!countDownLatch.await(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())) { logResult(TestResult.NOK, target, null, null, "(httpbin.org tests failed to complete in time)"); } } } enum TestResult {OK, NOK} private void logResult( final TestResult result, final HttpHost httpHost, final HttpRequest request, final HttpResponse response, final String message) { final StringBuilder buf = new StringBuilder(); buf.append(result); if (buf.length() == 2) { buf.append(" "); } buf.append(": ").append(httpHost).append(" "); if (response != null) { buf.append(response.getVersion()).append(" "); } if (request != null) { buf.append(request.getMethod()).append(" ").append(request.getRequestUri()); } if (message != null && !TextUtils.isBlank(message)) { buf.append(" -> ").append(message); } System.out.println(buf); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/0040775 0000000 0000000 00000000000 14442305417 023556 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/SingleLineResponseHandler.java0100664 0000000 0000000 00000006261 14245617503 031474 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncResponseBuilder; import org.apache.hc.core5.http.nio.support.BasicRequestConsumer; import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler; import org.apache.hc.core5.http.protocol.HttpContext; public class SingleLineResponseHandler extends BasicServerExchangeHandler> { public SingleLineResponseHandler(final String message) { super(new AsyncServerRequestHandler>() { @Override public AsyncRequestConsumer> prepare( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) throws HttpException { return new BasicRequestConsumer<>(entityDetails != null? new StringAsyncEntityConsumer() : null); } @Override public void handle( final Message requestMessage, final ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { responseTrigger.submitResponse( AsyncResponseBuilder.create(HttpStatus.SC_OK) .setEntity(message) .build(), context); } } ); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/JSSEProviderIntegrationTest.java0100664 0000000 0000000 00000030023 14403631147 031736 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.security.Provider; import java.security.SecureRandom; import java.security.Security; import java.util.concurrent.Future; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestValidateHost; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.ssl.SSLContextBuilder; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.conscrypt.Conscrypt; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.RegisterExtension; // @Parameterized.Parameters(name = "{0} {1}") // public static Collection protocols() { // return Arrays.asList(new Object[][]{ // {"Oracle", null}, // {"Conscrypt", "TLSv1.2"}, // {"Conscrypt", "TLSv1.3"}, // }); // } public abstract class JSSEProviderIntegrationTest { private final String securityProviderName; private final String protocolVersion; public JSSEProviderIntegrationTest(final String securityProviderName, final String protocolVersion) { super(); this.securityProviderName = securityProviderName; this.protocolVersion = protocolVersion; } private static final Timeout TIMEOUT = Timeout.ofMinutes(1); private static final int REQ_NUM = 25; private Provider securityProvider; class SecurityProviderResource implements BeforeEachCallback, AfterEachCallback { @Override public void beforeEach(final ExtensionContext context) throws Exception { if ("Conscrypt".equalsIgnoreCase(securityProviderName)) { try { securityProvider = Conscrypt.newProviderBuilder().provideTrustManager(true).build(); } catch (final UnsatisfiedLinkError e) { Assertions.fail("Conscrypt provider failed to be loaded: " + e.getMessage()); } } else { securityProvider = null; } if (securityProvider != null) { Security.insertProviderAt(securityProvider, 1); } } @Override public void afterEach(final ExtensionContext context) throws Exception { if (securityProvider != null) { Security.removeProvider(securityProvider.getName()); securityProvider = null; } } } @RegisterExtension @Order(1) private final SecurityProviderResource securityProviderResource = new SecurityProviderResource(); private Http1TestServer server; class ServerResource implements BeforeEachCallback, AfterEachCallback { @Override public void beforeEach(final ExtensionContext context) throws Exception { final URL keyStoreURL = getClass().getResource("/test-server.p12"); final String storePassword = "nopassword"; server = new Http1TestServer( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build(), SSLContextBuilder.create() .setProvider(securityProvider) .setKeyStoreType("pkcs12") .loadTrustMaterial(keyStoreURL, storePassword.toCharArray()) .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray()) .setSecureRandom(new SecureRandom()) .build(), (endpoint, sslEngine) -> { if (protocolVersion != null) { sslEngine.setEnabledProtocols(new String[]{protocolVersion}); } }, null); } @Override public void afterEach(final ExtensionContext context) throws Exception { if (server != null) { server.shutdown(TimeValue.ofSeconds(5)); } } } @RegisterExtension @Order(2) private final ServerResource serverResource = new ServerResource(); private Http1TestClient client; class ClientResource implements BeforeEachCallback, AfterEachCallback { @Override public void beforeEach(final ExtensionContext context) throws Exception { final URL keyStoreURL = getClass().getResource("/test-client.p12"); final String storePassword = "nopassword"; client = new Http1TestClient( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build(), SSLContextBuilder.create() .setProvider(securityProvider) .setKeyStoreType("pkcs12") .loadTrustMaterial(keyStoreURL, storePassword.toCharArray()) .setSecureRandom(new SecureRandom()) .build(), (endpoint, sslEngine) -> { if (protocolVersion != null) { sslEngine.setEnabledProtocols(new String[]{protocolVersion}); } }, null); } @Override public void afterEach(final ExtensionContext context) throws Exception { if (client != null) { client.shutdown(TimeValue.ofSeconds(5)); } } } @RegisterExtension @Order(3) private final ClientResource clientResource = new ClientResource(); private URI createRequestURI(final InetSocketAddress serverEndpoint, final String path) { try { return new URI("https", null, "localhost", serverEndpoint.getPort(), path, null, null); } catch (final URISyntaxException e) { throw new IllegalStateException(); } } @Test public void testSimpleGet() throws Exception { server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); for (int i = 0; i < REQ_NUM; i++) { final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); final String entity1 = result.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi there", entity1); } } @Test public void testSimpleGetConnectionClose() throws Exception { server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final URI requestURI = createRequestURI(serverEndpoint, "/hello"); for (int i = 0; i < REQ_NUM; i++) { final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); try (final ClientSessionEndpoint streamEndpoint = connectFuture.get()) { final Future> future = streamEndpoint.execute( AsyncRequestBuilder.get(requestURI) .addHeader(HttpHeaders.CONNECTION, "close") .build(), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); final String entity1 = result.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi there", entity1); } } } @Test public void testSimpleGetIdentityTransfer() throws Exception { server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final HttpProcessor httpProcessor = new DefaultHttpProcessor(new RequestValidateHost()); final InetSocketAddress serverEndpoint = server.start(httpProcessor, Http1Config.DEFAULT); client.start(); for (int i = 0; i < REQ_NUM; i++) { final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); try (final ClientSessionEndpoint streamEndpoint = connectFuture.get()) { final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); final String entity = result.getBody(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertEquals("Hi there", entity); } } } } ././@LongLink0100644 0000000 0000000 00000000146 14403631147 011637 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1SocksProxyCoreTransportTest.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1SocksProxyCoreTransportTest.jav0100664 0000000 0000000 00000012530 14403631147 032710 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.impl.bootstrap.StandardFilter; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncFilterChain; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.protocol.UriPatternMatcher; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.testing.extension.SocksProxyResource; import org.apache.hc.core5.testing.nio.extension.HttpAsyncRequesterResource; import org.apache.hc.core5.testing.nio.extension.HttpAsyncServerResource; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class Http1SocksProxyCoreTransportTest extends HttpCoreTransportTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension @Order(-Integer.MAX_VALUE) private final SocksProxyResource proxyResource; @RegisterExtension private final HttpAsyncServerResource serverResource; @RegisterExtension private final HttpAsyncRequesterResource clientResource; public Http1SocksProxyCoreTransportTest(final URIScheme scheme) { super(scheme); this.proxyResource = new SocksProxyResource(); this.serverResource = new HttpAsyncServerResource(bootstrap -> bootstrap .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setLookupRegistry(new UriPatternMatcher<>()) .register("*", () -> new EchoHandler(2048)) .addFilterBefore(StandardFilter.MAIN_HANDLER.name(), "no-keepalive", (request, entityDetails, context, responseTrigger, chain) -> chain.proceed(request, entityDetails, context, new AsyncFilterChain.ResponseTrigger() { @Override public void sendInformation( final HttpResponse response) throws HttpException, IOException { responseTrigger.sendInformation(response); } @Override public void submitResponse( final HttpResponse response, final AsyncEntityProducer entityProducer) throws HttpException, IOException { if (request.getPath().startsWith("/no-keep-alive")) { response.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); } responseTrigger.submitResponse(response, entityProducer); } @Override public void pushPromise( final HttpRequest promise, final AsyncPushProducer responseProducer) throws HttpException, IOException { responseTrigger.pushPromise(promise, responseProducer); } })) ); this.clientResource = new HttpAsyncRequesterResource(bootstrap -> bootstrap .setIOReactorConfig(IOReactorConfig.custom() .setSocksProxyAddress(proxyResource.proxy().getProxyAddress()) .setSoTimeout(TIMEOUT) .build()) ); } @Override HttpAsyncServer serverStart() throws IOException { return serverResource.start(); } @Override HttpAsyncRequester clientStart() { return clientResource.start(); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/H2SocksProxyCoreTransportTest.java0100664 0000000 0000000 00000006764 14403631147 032336 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.protocol.UriPatternMatcher; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.testing.extension.SocksProxyResource; import org.apache.hc.core5.testing.nio.extension.H2AsyncRequesterResource; import org.apache.hc.core5.testing.nio.extension.H2AsyncServerResource; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class H2SocksProxyCoreTransportTest extends HttpCoreTransportTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension @Order(-Integer.MAX_VALUE) private final SocksProxyResource proxyResource; @RegisterExtension private final H2AsyncServerResource serverResource; @RegisterExtension private final H2AsyncRequesterResource clientResource; public H2SocksProxyCoreTransportTest(final URIScheme scheme) { super(scheme); this.proxyResource = new SocksProxyResource(); this.serverResource = new H2AsyncServerResource(bootstrap -> bootstrap .setVersionPolicy(HttpVersionPolicy.NEGOTIATE) .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setLookupRegistry(new UriPatternMatcher<>()) .register("*", () -> new EchoHandler(2048)) ); this.clientResource = new H2AsyncRequesterResource(bootstrap -> bootstrap .setVersionPolicy(HttpVersionPolicy.NEGOTIATE) .setIOReactorConfig(IOReactorConfig.custom() .setSocksProxyAddress(proxyResource.proxy().getProxyAddress()) .setSoTimeout(TIMEOUT) .build()) ); } @Override HttpAsyncServer serverStart() throws IOException { return serverResource.start(); } @Override HttpAsyncRequester clientStart() { return clientResource.start(); } } ././@LongLink0100644 0000000 0000000 00000000145 14403631147 011636 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/H2CoreTransportMultiplexingTest.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/H2CoreTransportMultiplexingTest.java0100664 0000000 0000000 00000034053 14403631147 032663 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import static org.hamcrest.MatcherAssert.assertThat; import java.net.InetSocketAddress; import java.util.LinkedList; import java.util.Queue; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.BasicClientExchangeHandler; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.UriPatternMatcher; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2MultiplexingRequester; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.testing.nio.extension.H2AsyncServerResource; import org.apache.hc.core5.testing.nio.extension.H2MultiplexingRequesterResource; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class H2CoreTransportMultiplexingTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); private final URIScheme scheme; @RegisterExtension private final H2AsyncServerResource serverResource; @RegisterExtension private final H2MultiplexingRequesterResource clientResource; public H2CoreTransportMultiplexingTest(final URIScheme scheme) { this.scheme = scheme; this.serverResource = new H2AsyncServerResource(bootstrap -> bootstrap .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2) .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setLookupRegistry(new UriPatternMatcher<>()) .register("*", () -> new EchoHandler(2048)) ); this.clientResource = new H2MultiplexingRequesterResource(bootstrap -> bootstrap .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) ); } @Test public void testSequentialRequests() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), scheme); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final H2MultiplexingRequester requester = clientResource.start(); final HttpHost target = new HttpHost(scheme.id, "localhost", address.getPort()); final Future> resultFuture1 = requester.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = message1.getBody(); assertThat(body1, CoreMatchers.equalTo("some stuff")); final Future> resultFuture2 = requester.execute( new BasicRequestProducer(Method.POST, target, "/other-stuff", new StringAsyncEntityProducer("some other stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message2, CoreMatchers.notNullValue()); final HttpResponse response2 = message2.getHead(); assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = message2.getBody(); assertThat(body2, CoreMatchers.equalTo("some other stuff")); final Future> resultFuture3 = requester.execute( new BasicRequestProducer(Method.POST, target, "/more-stuff", new StringAsyncEntityProducer("some more stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message3 = resultFuture3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message3, CoreMatchers.notNullValue()); final HttpResponse response3 = message3.getHead(); assertThat(response3.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body3 = message3.getBody(); assertThat(body3, CoreMatchers.equalTo("some more stuff")); } @Test public void testMultiplexedRequests() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), scheme); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final H2MultiplexingRequester requester = clientResource.start(); final HttpHost target = new HttpHost(scheme.id, "localhost", address.getPort()); final Queue>> queue = new LinkedList<>(); queue.add(requester.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null)); queue.add(requester.execute( new BasicRequestProducer(Method.POST, target, "/other-stuff", new StringAsyncEntityProducer("some other stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null)); queue.add(requester.execute( new BasicRequestProducer(Method.POST, target, "/more-stuff", new StringAsyncEntityProducer("some more stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null)); while (!queue.isEmpty()) { final Future> resultFuture = queue.remove(); final Message message = resultFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message, CoreMatchers.notNullValue()); final HttpResponse response = message.getHead(); assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body = message.getBody(); assertThat(body, CoreMatchers.containsString("stuff")); } } @Test public void testValidityCheck() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), scheme); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final H2MultiplexingRequester requester = clientResource.start(); requester.setValidateAfterInactivity(TimeValue.ofMilliseconds(10)); final HttpHost target = new HttpHost(scheme.id, "localhost", address.getPort()); final Future> resultFuture1 = requester.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = message1.getBody(); assertThat(body1, CoreMatchers.equalTo("some stuff")); Thread.sleep(100); final Future> resultFuture2 = requester.execute( new BasicRequestProducer(Method.POST, target, "/other-stuff", new StringAsyncEntityProducer("some other stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message2, CoreMatchers.notNullValue()); final HttpResponse response2 = message2.getHead(); assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = message2.getBody(); assertThat(body2, CoreMatchers.equalTo("some other stuff")); Thread.sleep(100); final Future> resultFuture3 = requester.execute( new BasicRequestProducer(Method.POST, target, "/more-stuff", new StringAsyncEntityProducer("some more stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message3 = resultFuture3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message3, CoreMatchers.notNullValue()); final HttpResponse response3 = message3.getHead(); assertThat(response3.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body3 = message3.getBody(); assertThat(body3, CoreMatchers.equalTo("some more stuff")); } @Test public void testMultiplexedRequestCancellation() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), scheme); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final H2MultiplexingRequester requester = clientResource.start(); final int reqNo = 20; final CountDownLatch countDownLatch = new CountDownLatch(reqNo); final Random random = new Random(); final HttpHost target = new HttpHost(scheme.id, "localhost", address.getPort()); for (int i = 0; i < reqNo; i++) { final Cancellable cancellable = requester.execute( new BasicClientExchangeHandler<>(new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), new FutureCallback>() { @Override public void completed(final Message result) { countDownLatch.countDown(); } @Override public void failed(final Exception ex) { countDownLatch.countDown(); } @Override public void cancelled() { countDownLatch.countDown(); } }), TIMEOUT, HttpCoreContext.create()); Thread.sleep(random.nextInt(10)); cancellable.cancel(); } assertThat(countDownLatch.await(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()), CoreMatchers.equalTo(true)); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/HttpIntegrationTests.java0100664 0000000 0000000 00000010727 14403631147 030572 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import org.apache.hc.core5.http.URIScheme; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; public class HttpIntegrationTests { @Nested @DisplayName("Core transport (HTTP/1.1)") public class CoreTransport extends Http1CoreTransportTest { public CoreTransport() { super(URIScheme.HTTP); } } @Nested @DisplayName("Core transport (HTTP/1.1, TLS)") public class CoreTransportTls extends Http1CoreTransportTest { public CoreTransportTls() { super(URIScheme.HTTPS); } } @Nested @DisplayName("Core transport (H2)") public class CoreTransportH2 extends H2CoreTransportTest { public CoreTransportH2() { super(URIScheme.HTTP); } } @Nested @DisplayName("Core transport (H2, TLS)") public class CoreTransportH2Tls extends H2CoreTransportTest { public CoreTransportH2Tls() { super(URIScheme.HTTPS); } } @Nested @DisplayName("Core transport (H2, multiplexing)") public class CoreTransportH2Multiplexing extends H2CoreTransportMultiplexingTest { public CoreTransportH2Multiplexing() { super(URIScheme.HTTP); } } @Nested @DisplayName("Core transport (H2, multiplexing, TLS)") public class CoreTransportH2MultiplexingTls extends H2CoreTransportMultiplexingTest { public CoreTransportH2MultiplexingTls() { super(URIScheme.HTTPS); } } @Nested @DisplayName("Server filters") public class HttpFilters extends AsyncServerBootstrapFilterTest { public HttpFilters() { super(); } } @Nested @DisplayName("H2 Server filters") public class H2Filters extends H2ServerBootstrapFiltersTest { public H2Filters() { super(); } } @Nested @DisplayName("Authentication") public class Authentication extends Http1AuthenticationTest { public Authentication() { super(false); } } @Nested @DisplayName("Authentication (immediate response)") public class AuthenticationImmediateResponse extends Http1AuthenticationTest { public AuthenticationImmediateResponse() { super(true); } } @Nested @DisplayName("Core transport (HTTP/1.1, SOCKS)") public class CoreTransportSocksProxy extends Http1SocksProxyCoreTransportTest { public CoreTransportSocksProxy() { super(URIScheme.HTTP); } } @Nested @DisplayName("Core transport (HTTP/1.1, TLS, SOCKS)") public class CoreTransportSocksProxyTls extends Http1SocksProxyCoreTransportTest { public CoreTransportSocksProxyTls() { super(URIScheme.HTTPS); } } @Nested @DisplayName("Core transport (H2, SOCKS)") public class CoreTransportH2SocksProxy extends H2SocksProxyCoreTransportTest { public CoreTransportH2SocksProxy() { super(URIScheme.HTTP); } } @Nested @DisplayName("Core transport (H2, TLS, SOCKS)") public class CoreTransportH2SocksProxyTls extends H2SocksProxyCoreTransportTest { public CoreTransportH2SocksProxyTls() { super(URIScheme.HTTPS); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java0100664 0000000 0000000 00000262554 14403631147 030477 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import static org.hamcrest.MatcherAssert.assertThat; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Random; import java.util.StringTokenizer; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.MalformedChunkCodingException; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.nio.AbstractContentEncoder; import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexer; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.ContentEncoder; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.NHttpMessageParser; import org.apache.hc.core5.http.nio.NHttpMessageWriter; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.nio.entity.DigestingEntityConsumer; import org.apache.hc.core5.http.nio.entity.DigestingEntityProducer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.AbstractServerExchangeHandler; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator; import org.apache.hc.core5.http.nio.support.BasicRequestConsumer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.nio.support.BasicResponseProducer; import org.apache.hc.core5.http.nio.support.ImmediateResponseExchangeHandler; import org.apache.hc.core5.http.nio.support.classic.AbstractClassicEntityConsumer; import org.apache.hc.core5.http.nio.support.classic.AbstractClassicEntityProducer; import org.apache.hc.core5.http.nio.support.classic.AbstractClassicServerExchangeHandler; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestConnControl; import org.apache.hc.core5.http.protocol.RequestContent; import org.apache.hc.core5.http.protocol.RequestTargetHost; import org.apache.hc.core5.http.protocol.RequestValidateHost; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.nio.extension.Http1TestResources; import org.apache.hc.core5.util.CharArrayBuffer; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class Http1IntegrationTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); private static final Timeout LONG_TIMEOUT = Timeout.ofMinutes(2); private final URIScheme scheme; @RegisterExtension private final Http1TestResources resources; public Http1IntegrationTest(final URIScheme scheme) { this.scheme = scheme; this.resources = new Http1TestResources(scheme, TIMEOUT); } private URI createRequestURI(final InetSocketAddress serverEndpoint, final String path) { try { return new URI(scheme.id, null, "localhost", serverEndpoint.getPort(), path, null, null); } catch (final URISyntaxException e) { throw new IllegalStateException(); } } @Test public void testSimpleGet() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); for (int i = 0; i < 5; i++) { final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); final String entity1 = result.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi there", entity1); } } @Test public void testSimpleGetConnectionClose() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final URI requestURI = createRequestURI(serverEndpoint, "/hello"); for (int i = 0; i < 5; i++) { final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); try (final ClientSessionEndpoint streamEndpoint = connectFuture.get()) { final Future> future = streamEndpoint.execute( AsyncRequestBuilder.get(requestURI) .addHeader(HttpHeaders.CONNECTION, "close") .build(), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); final String entity1 = result.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi there", entity1); } } } @Test public void testSimpleGetIdentityTransfer() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final HttpProcessor httpProcessor = new DefaultHttpProcessor(new RequestValidateHost()); final InetSocketAddress serverEndpoint = server.start(httpProcessor, Http1Config.DEFAULT); client.start(); final int reqNo = 5; for (int i = 0; i < reqNo; i++) { final Future connectFuture = client.connect("localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); streamEndpoint.close(); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); final String entity = result.getBody(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertEquals("Hi there", entity); } } @Test public void testPostIdentityTransfer() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final HttpProcessor httpProcessor = new DefaultHttpProcessor(new RequestValidateHost()); final InetSocketAddress serverEndpoint = server.start(httpProcessor, Http1Config.DEFAULT); client.start(); final int reqNo = 5; for (int i = 0; i < reqNo; i++) { final Future connectFuture = client.connect("localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/hello"), new MultiLineEntityProducer("Hello", 16 * i)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); streamEndpoint.close(); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); final String entity = result.getBody(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertEquals("Hi there", entity); } } @Test public void testPostIdentityTransferOutOfSequenceResponse() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new ImmediateResponseExchangeHandler(500, "Go away")); final HttpProcessor httpProcessor = new DefaultHttpProcessor(new RequestValidateHost()); final InetSocketAddress serverEndpoint = server.start(httpProcessor, Http1Config.DEFAULT); client.start(); final int reqNo = 5; for (int i = 0; i < reqNo; i++) { final Future connectFuture = client.connect("localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/hello"), new MultiLineEntityProducer("Hello", 16 * i)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); streamEndpoint.close(); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); final String entity = result.getBody(); Assertions.assertNotNull(response); Assertions.assertEquals(500, response.getCode()); Assertions.assertEquals("Go away", entity); } } @Test public void testSimpleGetsPipelined() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Queue>> queue = new LinkedList<>(); for (int i = 0; i < 5; i++) { queue.add(streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); final String entity = result.getBody(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertEquals("Hi there", entity); } } @Test public void testLargeGet() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/", () -> new MultiLineResponseHandler("0123456789abcdef", 5000)); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); final String s1 = result1.getBody(); Assertions.assertNotNull(s1); final StringTokenizer t1 = new StringTokenizer(s1, "\r\n"); while (t1.hasMoreTokens()) { Assertions.assertEquals("0123456789abcdef", t1.nextToken()); } final Future> future2 = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer(512)), null); final Message result2 = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result2); final HttpResponse response2 = result2.getHead(); Assertions.assertNotNull(response2); Assertions.assertEquals(200, response2.getCode()); final String s2 = result2.getBody(); Assertions.assertNotNull(s2); final StringTokenizer t2 = new StringTokenizer(s2, "\r\n"); while (t2.hasMoreTokens()) { Assertions.assertEquals("0123456789abcdef", t2.nextToken()); } } @Test public void testLargeGetsPipelined() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/", () -> new MultiLineResponseHandler("0123456789abcdef", 2000)); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Queue>> queue = new LinkedList<>(); for (int i = 0; i < 5; i++) { queue.add(streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message result = future.get(LONG_TIMEOUT.getDuration(), LONG_TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); final String entity = result.getBody(); Assertions.assertNotNull(entity); final StringTokenizer t = new StringTokenizer(entity, "\r\n"); while (t.hasMoreTokens()) { Assertions.assertEquals("0123456789abcdef", t.nextToken()); } } } @Test public void testBasicPost() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi back")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); for (int i = 0; i < 5; i++) { final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/hello"), AsyncEntityProducers.create("Hi there")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); final String entity1 = result.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi back", entity1); } } @Test public void testBasicPostPipelined() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi back")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Queue>> queue = new LinkedList<>(); for (int i = 0; i < 5; i++) { queue.add(streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/hello"), AsyncEntityProducers.create("Hi there")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); final String entity = result.getBody(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertEquals("Hi back", entity); } } @Test public void testHttp10Post() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi back")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); for (int i = 0; i < 5; i++) { final HttpRequest request = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/hello")); request.setVersion(HttpVersion.HTTP_1_0); final Future> future = streamEndpoint.execute( new BasicRequestProducer(request, AsyncEntityProducers.create("Hi there")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); final String entity1 = result.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi back", entity1); } } @Test public void testNoEntityPost() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi back")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); for (int i = 0; i < 5; i++) { final HttpRequest request = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/hello")); final Future> future = streamEndpoint.execute( new BasicRequestProducer(request, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); final String entity1 = result.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi back", entity1); } } @Test public void testLargePost() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("*", () -> new EchoHandler(2048)); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); for (int i = 0; i < 5; i++) { final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/echo"), new MultiLineEntityProducer("0123456789abcdef", 5000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); final String entity = result.getBody(); Assertions.assertNotNull(entity); final StringTokenizer t = new StringTokenizer(entity, "\r\n"); while (t.hasMoreTokens()) { Assertions.assertEquals("0123456789abcdef", t.nextToken()); } } } @Test public void testPostsPipelinedLargeResponse() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/", () -> new MultiLineResponseHandler("0123456789abcdef", 2000)); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Queue>> queue = new LinkedList<>(); for (int i = 0; i < 2; i++) { queue.add(streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/"), AsyncEntityProducers.create("Hi there")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); final String entity = result.getBody(); Assertions.assertNotNull(entity); final StringTokenizer t = new StringTokenizer(entity, "\r\n"); while (t.hasMoreTokens()) { Assertions.assertEquals("0123456789abcdef", t.nextToken()); } } } @Test public void testLargePostsPipelined() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("*", () -> new EchoHandler(2048)); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Queue>> queue = new LinkedList<>(); for (int i = 0; i < 5; i++) { queue.add(streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/echo"), new MultiLineEntityProducer("0123456789abcdef", 5000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); final String entity = result.getBody(); Assertions.assertNotNull(entity); final StringTokenizer t = new StringTokenizer(entity, "\r\n"); while (t.hasMoreTokens()) { Assertions.assertEquals("0123456789abcdef", t.nextToken()); } } } @Test public void testSimpleHead() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); for (int i = 0; i < 5; i++) { final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.HEAD, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertNull(result.getBody()); } } @Test public void testSimpleHeadConnectionClose() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final URI requestURI = createRequestURI(serverEndpoint, "/hello"); for (int i = 0; i < 5; i++) { final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); try (final ClientSessionEndpoint streamEndpoint = connectFuture.get()) { final Future> future = streamEndpoint.execute( AsyncRequestBuilder.head(requestURI) .addHeader(HttpHeaders.CONNECTION, "close") .build(), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertNull(result.getBody()); } } } @Test public void testHeadPipelined() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Queue>> queue = new LinkedList<>(); for (int i = 0; i < 5; i++) { queue.add(streamEndpoint.execute( new BasicRequestProducer(Method.HEAD, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertNull(result.getBody()); } } @Test public void testExpectationFailed() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("*", () -> new MessageExchangeHandler(new StringAsyncEntityConsumer()) { @Override protected void handle( final Message request, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws IOException, HttpException { responseTrigger.submitResponse(new BasicResponseProducer(HttpStatus.SC_OK, "All is well"), context); } }); final InetSocketAddress serverEndpoint = server.start(null, handler -> new BasicAsyncServerExpectationDecorator(handler) { @Override protected AsyncResponseProducer verify(final HttpRequest request, final HttpContext context) throws IOException, HttpException { final Header h = request.getFirstHeader("password"); if (h != null && "secret".equals(h.getValue())) { return null; } else { return new BasicResponseProducer(HttpStatus.SC_UNAUTHORIZED, "You shall not pass"); } } }, Http1Config.DEFAULT); client.start(); final Future sessionFuture = client.requestSession( new HttpHost("localhost", serverEndpoint.getPort()), TIMEOUT, null); final IOSession ioSession = sessionFuture.get(); try (final ClientSessionEndpoint streamEndpoint = new ClientSessionEndpoint(ioSession)) { final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); request1.addHeader("password", "secret"); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, new MultiLineEntityProducer("0123456789abcdef", 1000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("All is well", result1.getBody()); Assertions.assertTrue(ioSession.isOpen()); final HttpRequest request2 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); final Future> future2 = streamEndpoint.execute( new BasicRequestProducer(request2, new MultiLineEntityProducer("0123456789abcdef", 5000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result2 = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result2); final HttpResponse response2 = result2.getHead(); Assertions.assertNotNull(response2); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response2.getCode()); Assertions.assertEquals("You shall not pass", result2.getBody()); Assertions.assertTrue(ioSession.isOpen()); final HttpRequest request3 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); request3.addHeader("password", "secret"); final Future> future3 = streamEndpoint.execute( new BasicRequestProducer(request3, new MultiLineEntityProducer("0123456789abcdef", 1000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result3 = future3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result3); final HttpResponse response3 = result3.getHead(); Assertions.assertNotNull(response3); Assertions.assertEquals(200, response3.getCode()); Assertions.assertEquals("All is well", result3.getBody()); Assertions.assertTrue(ioSession.isOpen()); final HttpRequest request4 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); final Future> future4 = streamEndpoint.execute( new BasicRequestProducer(request4, AsyncEntityProducers.create("blah")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result4 = future4.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result4); final HttpResponse response4 = result4.getHead(); Assertions.assertNotNull(response4); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response4.getCode()); Assertions.assertEquals("You shall not pass", result4.getBody()); Assertions.assertFalse(ioSession.isOpen()); } } @Test public void testExpectationFailedCloseConnection() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("*", () -> new MessageExchangeHandler(new StringAsyncEntityConsumer()) { @Override protected void handle( final Message request, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws IOException, HttpException { responseTrigger.submitResponse(new BasicResponseProducer(HttpStatus.SC_OK, "All is well"), context); } }); final InetSocketAddress serverEndpoint = server.start(null, handler -> new BasicAsyncServerExpectationDecorator(handler) { @Override protected AsyncResponseProducer verify(final HttpRequest request, final HttpContext context) throws IOException, HttpException { final Header h = request.getFirstHeader("password"); if (h != null && "secret".equals(h.getValue())) { return null; } else { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED); response.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); return new BasicResponseProducer(response, "You shall not pass"); } } }, Http1Config.DEFAULT); client.start(); final Future sessionFuture = client.requestSession( new HttpHost("localhost", serverEndpoint.getPort()), TIMEOUT, null); final IOSession ioSession = sessionFuture.get(); try (final ClientSessionEndpoint streamEndpoint = new ClientSessionEndpoint(ioSession)) { final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, new MultiBinEntityProducer( new byte[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}, 100000, ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response1.getCode()); Assertions.assertNotNull("You shall not pass", result1.getBody()); Assertions.assertFalse(streamEndpoint.isOpen()); } } @Test public void testDelayedExpectationVerification() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("*", () -> new AsyncServerExchangeHandler() { private final Random random = new Random(System.currentTimeMillis()); private final AsyncEntityProducer entityProducer = AsyncEntityProducers.create( "All is well"); @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { Executors.newSingleThreadExecutor().execute(() -> { try { if (entityDetails != null) { final Header h = request.getFirstHeader(HttpHeaders.EXPECT); if (h != null && HeaderElements.CONTINUE.equalsIgnoreCase(h.getValue())) { Thread.sleep(random.nextInt(1000)); responseChannel.sendInformation(new BasicHttpResponse(HttpStatus.SC_CONTINUE), context); } final HttpResponse response = new BasicHttpResponse(200); synchronized (entityProducer) { responseChannel.sendResponse(response, entityProducer, context); } } } catch (final Exception ignore) { // ignore } }); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(Integer.MAX_VALUE); } @Override public void consume(final ByteBuffer src) throws IOException { } @Override public void streamEnd(final List trailers) throws HttpException, IOException { } @Override public int available() { synchronized (entityProducer) { return entityProducer.available(); } } @Override public void produce(final DataStreamChannel channel) throws IOException { synchronized (entityProducer) { entityProducer.produce(channel); } } @Override public void failed(final Exception cause) { } @Override public void releaseResources() { } }); final InetSocketAddress serverEndpoint = server.start(); client.start(Http1Config.custom().setWaitForContinueTimeout(Timeout.ofMilliseconds(100)).build()); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Queue>> queue = new LinkedList<>(); for (int i = 0; i < 5; i++) { queue.add(streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/"), AsyncEntityProducers.create("Some important message")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message result = future.get(LONG_TIMEOUT.getDuration(), LONG_TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertNotNull("All is well", result.getBody()); } } @Test public void testPrematureResponse() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("*", () -> new AsyncServerExchangeHandler() { private final AtomicReference responseProducer = new AtomicReference<>(); @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final AsyncResponseProducer producer; final Header h = request.getFirstHeader("password"); if (h != null && "secret".equals(h.getValue())) { producer = new BasicResponseProducer(HttpStatus.SC_OK, "All is well"); } else { producer = new BasicResponseProducer(HttpStatus.SC_UNAUTHORIZED, "You shall not pass"); } responseProducer.set(producer); producer.sendResponse(responseChannel, context); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(Integer.MAX_VALUE); } @Override public void consume(final ByteBuffer src) throws IOException { } @Override public void streamEnd(final List trailers) throws HttpException, IOException { } @Override public int available() { final AsyncResponseProducer producer = responseProducer.get(); return producer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { final AsyncResponseProducer producer = responseProducer.get(); producer.produce(channel); } @Override public void failed(final Exception cause) { } @Override public void releaseResources() { } }); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); for (int i = 0; i < 3; i++) { final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, new MultiLineEntityProducer("0123456789abcdef", 100000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response1.getCode()); Assertions.assertNotNull("You shall not pass", result1.getBody()); Assertions.assertTrue(streamEndpoint.isOpen()); } final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, new MultiBinEntityProducer( new byte[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}, 100000, ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response1.getCode()); Assertions.assertNotNull("You shall not pass", result1.getBody()); } @Test public void testSlowResponseConsumer() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/", () -> new MultiLineResponseHandler("0123456789abcd", 100)); final InetSocketAddress serverEndpoint = server.start(); client.start(Http1Config.custom().setBufferSize(256).build()); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/")); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, null), new BasicResponseConsumer<>(new AbstractClassicEntityConsumer(16, Executors.newSingleThreadExecutor()) { @Override protected String consumeData( final ContentType contentType, final InputStream inputStream) throws IOException { final Charset charset = ContentType.getCharset(contentType, StandardCharsets.US_ASCII); final StringBuilder buffer = new StringBuilder(); try { final byte[] tmp = new byte[16]; int l; while ((l = inputStream.read(tmp)) != -1) { buffer.append(charset.decode(ByteBuffer.wrap(tmp, 0, l))); Thread.sleep(50); } } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); throw new InterruptedIOException(ex.getMessage()); } return buffer.toString(); } }), null); final Message result1 = future1.get(LONG_TIMEOUT.getDuration(), LONG_TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); final String s1 = result1.getBody(); Assertions.assertNotNull(s1); final StringTokenizer t1 = new StringTokenizer(s1, "\r\n"); while (t1.hasMoreTokens()) { Assertions.assertEquals("0123456789abcd", t1.nextToken()); } } @Test public void testSlowRequestProducer() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("*", () -> new EchoHandler(2048)); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, new AbstractClassicEntityProducer(4096, ContentType.TEXT_PLAIN, Executors.newSingleThreadExecutor()) { @Override protected void produceData(final ContentType contentType, final OutputStream outputStream) throws IOException { final Charset charset = ContentType.getCharset(contentType, StandardCharsets.US_ASCII); try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, charset))) { for (int i = 0; i < 500; i++) { if (i % 100 == 0) { writer.flush(); Thread.sleep(500); } writer.write("0123456789abcdef\r\n"); } } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); throw new InterruptedIOException(ex.getMessage()); } } }), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(LONG_TIMEOUT.getDuration(), LONG_TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); final String s1 = result1.getBody(); Assertions.assertNotNull(s1); final StringTokenizer t1 = new StringTokenizer(s1, "\r\n"); while (t1.hasMoreTokens()) { Assertions.assertEquals("0123456789abcdef", t1.nextToken()); } } @Test public void testSlowResponseProducer() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("*", () -> new AbstractClassicServerExchangeHandler(2048, Executors.newSingleThreadExecutor()) { @Override protected void handle( final HttpRequest request, final InputStream requestStream, final HttpResponse response, final OutputStream responseStream, final HttpContext context) throws IOException, HttpException { if (!"/hello".equals(request.getPath())) { response.setCode(HttpStatus.SC_NOT_FOUND); return; } if (!Method.POST.name().equalsIgnoreCase(request.getMethod())) { response.setCode(HttpStatus.SC_NOT_IMPLEMENTED); return; } if (requestStream == null) { return; } final Header h1 = request.getFirstHeader(HttpHeaders.CONTENT_TYPE); final ContentType contentType = h1 != null ? ContentType.parse(h1.getValue()) : null; final Charset charset = ContentType.getCharset(contentType, StandardCharsets.US_ASCII); response.setCode(HttpStatus.SC_OK); response.setHeader(h1); try (final BufferedReader reader = new BufferedReader(new InputStreamReader(requestStream, charset)); final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(responseStream, charset))) { try { String l; int count = 0; while ((l = reader.readLine()) != null) { writer.write(l); writer.write("\r\n"); count++; if (count % 500 == 0) { Thread.sleep(500); } } writer.flush(); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); throw new InterruptedIOException(ex.getMessage()); } } } }); final InetSocketAddress serverEndpoint = server.start(); client.start(Http1Config.custom().setBufferSize(256).build()); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/hello")); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, new MultiLineEntityProducer("0123456789abcd", 2000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(LONG_TIMEOUT.getDuration(), LONG_TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); final String s1 = result1.getBody(); Assertions.assertNotNull(s1); final StringTokenizer t1 = new StringTokenizer(s1, "\r\n"); while (t1.hasMoreTokens()) { Assertions.assertEquals("0123456789abcd", t1.nextToken()); } } @Test public void testPipelinedConnectionClose() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello*", () -> new SingleLineResponseHandler("Hi back")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/hello-1"), AsyncEntityProducers.create("Hi there")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final HttpRequest request2 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/hello-2")); request2.addHeader(HttpHeaders.CONNECTION, "close"); final Future> future2 = streamEndpoint.execute( new BasicRequestProducer(request2, AsyncEntityProducers.create("Hi there")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Future> future3 = streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/hello-3"), AsyncEntityProducers.create("Hi there")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); final String entity1 = result1.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi back", entity1); final Message result2 = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result2); final HttpResponse response2 = result2.getHead(); final String entity2 = result2.getBody(); Assertions.assertNotNull(response2); Assertions.assertEquals(200, response2.getCode()); Assertions.assertEquals("Hi back", entity2); final Exception exception = Assertions.assertThrows(Exception.class, () -> future3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); assertThat(exception, CoreMatchers.anyOf( CoreMatchers.instanceOf(CancellationException.class), CoreMatchers.instanceOf(ExecutionException.class))); final Future> future4 = streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/hello-3"), AsyncEntityProducers.create("Hi there")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Exception exception2 = Assertions.assertThrows(Exception.class, () -> future4.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); assertThat(exception2, CoreMatchers.anyOf( CoreMatchers.instanceOf(CancellationException.class), CoreMatchers.instanceOf(ExecutionException.class))); } @Test public void testPipelinedInvalidRequest() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello*", () -> new SingleLineResponseHandler("Hi back")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/hello-1"), AsyncEntityProducers.create("Hi there")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final HttpRequest request2 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/hello-2")); request2.addHeader(HttpHeaders.HOST, "blah:blah"); final Future> future2 = streamEndpoint.execute( new BasicRequestProducer(request2, AsyncEntityProducers.create("Hi there")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Future> future3 = streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/hello-3"), AsyncEntityProducers.create("Hi there")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); final String entity1 = result1.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi back", entity1); final Message result2 = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result2); final HttpResponse response2 = result2.getHead(); final String entity2 = result2.getBody(); Assertions.assertNotNull(response2); Assertions.assertEquals(400, response2.getCode()); Assertions.assertTrue(entity2.length() > 0); final Exception exception = Assertions.assertThrows(Exception.class, () -> future3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); assertThat(exception, CoreMatchers.anyOf( CoreMatchers.instanceOf(CancellationException.class), CoreMatchers.instanceOf(ExecutionException.class))); } private static final byte[] GARBAGE = "garbage".getBytes(StandardCharsets.US_ASCII); private static class BrokenChunkEncoder extends AbstractContentEncoder { private final CharArrayBuffer lineBuffer; private boolean done; BrokenChunkEncoder( final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics) { super(channel, buffer, metrics); lineBuffer = new CharArrayBuffer(16); } @Override public void complete(final List trailers) throws IOException { super.complete(trailers); } @Override public int write(final ByteBuffer src) throws IOException { final int chunk; if (!done) { lineBuffer.clear(); lineBuffer.append(Integer.toHexString(GARBAGE.length * 10)); buffer().writeLine(lineBuffer); buffer().write(ByteBuffer.wrap(GARBAGE)); done = true; chunk = GARBAGE.length; } else { chunk = 0; } final long bytesWritten = buffer().flush(channel()); if (bytesWritten > 0) { metrics().incrementBytesTransferred(bytesWritten); } if (!buffer().hasData()) { channel().close(); } return chunk; } } @Test public void testTruncatedChunk() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); final InetSocketAddress serverEndpoint = server.start(new InternalServerHttp1EventHandlerFactory( HttpProcessors.server(), (request, context) -> new MessageExchangeHandler(new StringAsyncEntityConsumer()) { @Override protected void handle( final Message request, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws IOException, HttpException { responseTrigger.submitResponse( new BasicResponseProducer(new StringAsyncEntityProducer("useful stuff")), context); } }, Http1Config.DEFAULT, CharCodingConfig.DEFAULT, DefaultConnectionReuseStrategy.INSTANCE, scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null, null, null) { @Override protected ServerHttp1StreamDuplexer createServerHttp1StreamDuplexer( final ProtocolIOSession ioSession, final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final Http1Config http1Config, final CharCodingConfig connectionConfig, final ConnectionReuseStrategy connectionReuseStrategy, final NHttpMessageParser incomingMessageParser, final NHttpMessageWriter outgoingMessageWriter, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final Http1StreamListener streamListener) { return new ServerHttp1StreamDuplexer(ioSession, httpProcessor, exchangeHandlerFactory, scheme.id, http1Config, connectionConfig, connectionReuseStrategy, incomingMessageParser, outgoingMessageWriter, incomingContentStrategy, outgoingContentStrategy, streamListener) { @Override protected ContentEncoder createContentEncoder( final long len, final WritableByteChannel channel, final SessionOutputBuffer buffer, final BasicHttpTransportMetrics metrics) throws HttpException { if (len == ContentLengthStrategy.CHUNKED) { return new BrokenChunkEncoder(channel, buffer, metrics); } else { return super.createContentEncoder(len, channel, buffer, metrics); } } }; } }); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final AsyncRequestProducer requestProducer = new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")); final StringAsyncEntityConsumer entityConsumer = new StringAsyncEntityConsumer() { @Override public void releaseResources() { // Do not clear internal content buffer } }; final BasicResponseConsumer responseConsumer = new BasicResponseConsumer<>(entityConsumer); final Future> future1 = streamEndpoint.execute(requestProducer, responseConsumer, null); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); final Throwable cause = exception.getCause(); Assertions.assertTrue(cause instanceof MalformedChunkCodingException); Assertions.assertEquals("garbage", entityConsumer.generateContent()); } @Test public void testExceptionInHandler() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there") { @Override protected void handle( final Message request, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws IOException, HttpException { throw new HttpException("Boom"); } }); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); final String entity1 = result.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(500, response1.getCode()); Assertions.assertEquals("Boom", entity1); } @Test public void testNoServiceHandler() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); final String entity1 = result.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(404, response1.getCode()); Assertions.assertEquals("Resource not found", entity1); } @Test public void testResponseNoContent() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there") { @Override protected void handle( final Message request, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws IOException, HttpException { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_NO_CONTENT); responseTrigger.submitResponse(new BasicResponseProducer(response), context); } }); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(204, response1.getCode()); Assertions.assertNull(result.getBody()); } @Test public void testAbsentHostHeader() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(new DefaultHttpProcessor(RequestContent.INSTANCE, RequestConnControl.INSTANCE), Http1Config.DEFAULT); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/hello")); request1.setVersion(HttpVersion.HTTP_1_0); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi there", result1.getBody()); final HttpRequest request2 = new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/hello")); request2.setVersion(HttpVersion.HTTP_1_1); final Future> future2 = streamEndpoint.execute( new BasicRequestProducer(request2, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result2 = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result2); final HttpResponse response2 = result2.getHead(); Assertions.assertNotNull(response2); Assertions.assertEquals(400, response2.getCode()); Assertions.assertEquals("Host header is absent", result2.getBody()); } @Test public void testMessageWithTrailers() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new AbstractServerExchangeHandler>() { @Override protected AsyncRequestConsumer> supplyConsumer( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) throws HttpException { return new BasicRequestConsumer<>(entityDetails != null ? new StringAsyncEntityConsumer() : null); } @Override protected void handle( final Message requestMessage, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { responseTrigger.submitResponse(new BasicResponseProducer( HttpStatus.SC_OK, new DigestingEntityProducer("MD5", new StringAsyncEntityProducer("Hello back with some trailers"))), context); } }); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/hello")); final DigestingEntityConsumer entityConsumer = new DigestingEntityConsumer<>("MD5", new StringAsyncEntityConsumer()); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, null), new BasicResponseConsumer<>(entityConsumer), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hello back with some trailers", result1.getBody()); final List
trailers = entityConsumer.getTrailers(); Assertions.assertNotNull(trailers); Assertions.assertEquals(2, trailers.size()); final Map map = new HashMap<>(); for (final Header header: trailers) { map.put(TextUtils.toLowerCase(header.getName()), header.getValue()); } final String digest = TextUtils.toHexString(entityConsumer.getDigest()); Assertions.assertEquals("MD5", map.get("digest-algo")); Assertions.assertEquals(digest, map.get("digest")); } @Test public void testProtocolException() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/boom", () -> new AsyncServerExchangeHandler() { private final StringAsyncEntityProducer entityProducer = new StringAsyncEntityProducer("Everyting is OK"); @Override public void releaseResources() { entityProducer.releaseResources(); } @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final String requestUri = request.getRequestUri(); if (requestUri.endsWith("boom")) { throw new ProtocolException("Boom!!!"); } responseChannel.sendResponse(new BasicHttpResponse(200), entityProducer, context); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(Integer.MAX_VALUE); } @Override public void consume(final ByteBuffer src) throws IOException { } @Override public void streamEnd(final List trailers) throws HttpException, IOException { // empty } @Override public int available() { return entityProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { entityProducer.produce(channel); } @Override public void failed(final Exception cause) { releaseResources(); } }); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/boom")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); final String entity1 = result.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST, response1.getCode()); Assertions.assertEquals("Boom!!!", entity1); } @Test public void testHeaderTooLarge() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(null, Http1Config.custom() .setMaxLineLength(100) .build()); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/hello")); request1.setHeader("big-f-header", "1234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890"); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(431, response1.getCode()); Assertions.assertEquals("Maximum line length limit exceeded", result1.getBody()); } @Test public void testHeaderTooLargePost() throws Exception { final Http1TestServer server = resources.server(); final Http1TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(null, Http1Config.custom() .setMaxLineLength(100) .build()); client.start( new DefaultHttpProcessor(RequestContent.INSTANCE, RequestTargetHost.INSTANCE, RequestConnControl.INSTANCE), null); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/hello")); request1.setHeader("big-f-header", "1234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890"); final byte[] b = new byte[2048]; for (int i = 0; i < b.length; i++) { b[i] = (byte) ('a' + i % 10); } final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, AsyncEntityProducers.create(b, ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(431, response1.getCode()); Assertions.assertEquals("Maximum line length limit exceeded", result1.getBody()); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/TLSIntegrationTest.java0100664 0000000 0000000 00000044247 14403631147 030136 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.stream.Stream; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.concurrent.FutureContribution; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.AsyncRequesterBootstrap; import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.ssl.BasicClientTlsStrategy; import org.apache.hc.core5.http.nio.ssl.BasicServerTlsStrategy; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.nio.ssl.TlsSupport; import org.apache.hc.core5.http.nio.ssl.TlsUpgradeCapable; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.UriPatternMatcher; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.classic.LoggingConnPoolListener; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ReflectionUtils; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.ValueSource; public class TLSIntegrationTest { private static final Timeout TIMEOUT = Timeout.ofSeconds(30); private HttpAsyncServer server; @RegisterExtension public final AfterEachCallback serverCleanup = new AfterEachCallback() { @Override public void afterEach(final ExtensionContext context) throws Exception { if (server != null) { try { server.close(CloseMode.IMMEDIATE); } catch (final Exception ignore) { } } } }; private HttpAsyncRequester client; @RegisterExtension public final AfterEachCallback clientCleanup = new AfterEachCallback() { @Override public void afterEach(final ExtensionContext context) throws Exception { if (client != null) { try { client.close(CloseMode.GRACEFUL); } catch (final Exception ignore) { } } } }; HttpAsyncServer createServer(final TlsStrategy tlsStrategy) { return AsyncServerBootstrap.bootstrap() .setLookupRegistry(new UriPatternMatcher<>()) .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .setIoThreadCount(1) .build()) .setTlsStrategy(tlsStrategy) .setStreamListener(LoggingHttp1StreamListener.INSTANCE_SERVER) .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE) .setExceptionCallback(LoggingExceptionCallback.INSTANCE) .setIOSessionListener(LoggingIOSessionListener.INSTANCE) .register("*", () -> new EchoHandler(2048)) .create(); } HttpAsyncRequester createClient(final TlsStrategy tlsStrategy) { return AsyncRequesterBootstrap.bootstrap() .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setTlsStrategy(tlsStrategy) .setStreamListener(LoggingHttp1StreamListener.INSTANCE_CLIENT) .setConnPoolListener(LoggingConnPoolListener.INSTANCE) .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE) .setExceptionCallback(LoggingExceptionCallback.INSTANCE) .setIOSessionListener(LoggingIOSessionListener.INSTANCE) .create(); } Future executeTlsHandshake() throws Exception { final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTPS); final ListenerEndpoint listener = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpHost target = new HttpHost(URIScheme.HTTPS.id, "localhost", address.getPort()); final BasicFuture tlsFuture = new BasicFuture<>(null); client.connect( new HttpHost(URIScheme.HTTP.id, "localhost", address.getPort()), TIMEOUT, null, new FutureContribution(tlsFuture) { @Override public void completed(final AsyncClientEndpoint clientEndpoint) { try { ((TlsUpgradeCapable) clientEndpoint).tlsUpgrade( target, new FutureContribution(tlsFuture) { @Override public void completed(final ProtocolIOSession protocolIOSession) { tlsFuture.completed(protocolIOSession.getTlsDetails()); } }); } catch (final Exception ex) { tlsFuture.failed(ex); } } }); return tlsFuture; } @ParameterizedTest(name = "TLS protocol {0}") @ArgumentsSource(SupportedTLSProtocolProvider.class) public void testTLSSuccess(final TLS tlsProtocol) throws Exception { final TlsStrategy serverTlsStrategy = new TestTlsStrategy( SSLTestContexts.createServerSSLContext(), (endpoint, sslEngine) -> sslEngine.setEnabledProtocols(new String[]{tlsProtocol.getId()}), null); server = createServer(serverTlsStrategy); server.start(); final TlsStrategy clientTlsStrategy = new TestTlsStrategy(SSLTestContexts.createClientSSLContext(), (endpoint, sslEngine) -> sslEngine.setEnabledProtocols(new String[]{tlsProtocol.getId()}), null); client = createClient(clientTlsStrategy); client.start(); final Future tlsSessionFuture = executeTlsHandshake(); final TlsDetails tlsDetails = tlsSessionFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(tlsDetails); final SSLSession tlsSession = tlsDetails.getSSLSession(); final ProtocolVersion tlsVersion = TLS.parse(tlsSession.getProtocol()); MatcherAssert.assertThat(tlsVersion.greaterEquals(tlsProtocol.version), CoreMatchers.equalTo(true)); MatcherAssert.assertThat(tlsSession.getPeerPrincipal().getName(), CoreMatchers.equalTo("CN=localhost,OU=Apache HttpComponents,O=Apache Software Foundation")); } @Test public void testTLSTrustFailure() throws Exception { final TlsStrategy serverTlsStrategy = new BasicServerTlsStrategy(SSLTestContexts.createServerSSLContext()); server = createServer(serverTlsStrategy); server.start(); final TlsStrategy clientTlsStrategy = new BasicClientTlsStrategy(SSLContexts.createDefault()); client = createClient(clientTlsStrategy); client.start(); final Future tlsSessionFuture = executeTlsHandshake(); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> tlsSessionFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); final Throwable cause = exception.getCause(); Assertions.assertInstanceOf(SSLHandshakeException.class, cause); } @Test public void testTLSClientAuthFailure() throws Exception { final TlsStrategy serverTlsStrategy = new BasicServerTlsStrategy( SSLTestContexts.createServerSSLContext(), (endpoint, sslEngine) -> sslEngine.setNeedClientAuth(true), null); server = createServer(serverTlsStrategy); server.start(); final TlsStrategy clientTlsStrategy = new BasicClientTlsStrategy(SSLTestContexts.createClientSSLContext()); client = createClient(clientTlsStrategy); client.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTPS); final ListenerEndpoint listener = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpHost target = new HttpHost(URIScheme.HTTPS.id, "localhost", address.getPort()); final Future> resultFuture = client.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> resultFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); final Throwable cause = exception.getCause(); Assertions.assertInstanceOf(IOException.class, cause); } @Test public void testSSLDisabledByDefault() throws Exception { final TlsStrategy serverTlsStrategy = new TestTlsStrategy( SSLTestContexts.createServerSSLContext(), (endpoint, sslEngine) -> sslEngine.setEnabledProtocols(new String[]{"SSLv3"}), null); server = createServer(serverTlsStrategy); server.start(); final TlsStrategy clientTlsStrategy = new BasicClientTlsStrategy(SSLTestContexts.createClientSSLContext()); client = createClient(clientTlsStrategy); client.start(); final Future tlsSessionFuture = executeTlsHandshake(); Assertions.assertThrows(ExecutionException.class, () -> tlsSessionFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); } @ParameterizedTest(name = "cipher {0}") @ValueSource(strings = { "SSL_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_128_CBC_SHA", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_RSA_WITH_NULL_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_256_GCM_SHA384", "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_NULL_SHA256", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5" }) public void testWeakCipherDisabledByDefault(final String cipher) throws Exception { final TlsStrategy serverTlsStrategy = new TestTlsStrategy( SSLTestContexts.createServerSSLContext(), (endpoint, sslEngine) -> sslEngine.setEnabledCipherSuites(new String[]{cipher}), null); server = createServer(serverTlsStrategy); server.start(); final TlsStrategy clientTlsStrategy = new BasicClientTlsStrategy(SSLTestContexts.createClientSSLContext()); client = createClient(clientTlsStrategy); client.start(); final Future tlsSessionFuture = executeTlsHandshake(); Assertions.assertThrows(ExecutionException.class, () -> tlsSessionFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); } @Test public void testTLSVersionMismatch() throws Exception { final TlsStrategy serverTlsStrategy = new TestTlsStrategy( SSLTestContexts.createServerSSLContext(), (endpoint, sslEngine) -> { sslEngine.setEnabledProtocols(new String[]{TLS.V_1_0.getId()}); sslEngine.setEnabledCipherSuites(new String[]{ "TLS_RSA_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_3DES_EDE_CBC_SHA"}); }, null); server = createServer(serverTlsStrategy); server.start(); final TlsStrategy clientTlsStrategy = new BasicClientTlsStrategy( SSLTestContexts.createClientSSLContext(), (endpoint, sslEngine) -> sslEngine.setEnabledProtocols(new String[]{TLS.V_1_2.getId()}), null); client = createClient(clientTlsStrategy); client.start(); final Future tlsSessionFuture = executeTlsHandshake(); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> tlsSessionFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); final Throwable cause = exception.getCause(); Assertions.assertInstanceOf(IOException.class, cause); } static class SupportedTLSProtocolProvider implements ArgumentsProvider { int javaVere = ReflectionUtils.determineJRELevel(); @Override public Stream provideArguments(final ExtensionContext context) { if (javaVere >= 11) { return Stream.of(Arguments.of(TLS.V_1_2), Arguments.of(TLS.V_1_3)); } else { return Stream.of(Arguments.of(TLS.V_1_2)); } } } static class TestTlsStrategy implements TlsStrategy { private final SSLContext sslContext; private final SSLSessionInitializer initializer; private final SSLSessionVerifier verifier; public TestTlsStrategy( final SSLContext sslContext, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this.sslContext = Args.notNull(sslContext, "SSL context"); this.initializer = initializer; this.verifier = verifier; } @Override public void upgrade( final TransportSecurityLayer tlsSession, final NamedEndpoint endpoint, final Object attachment, final Timeout handshakeTimeout, final FutureCallback callback) { tlsSession.startTls(sslContext, endpoint, SSLBufferMode.STATIC, TlsSupport.enforceStrongSecurity(initializer), verifier, handshakeTimeout, callback); } /** * @deprecated do not use. */ @Deprecated @Override public boolean upgrade( final TransportSecurityLayer tlsSession, final HttpHost host, final SocketAddress localAddress, final SocketAddress remoteAddress, final Object attachment, final Timeout handshakeTimeout) { tlsSession.startTls(sslContext, host, SSLBufferMode.STATIC, TlsSupport.enforceStrongSecurity(initializer), verifier, handshakeTimeout, null); return true; } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/H2AlpnTests.java0100664 0000000 0000000 00000003770 14403631147 026533 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; public class H2AlpnTests { @Nested @DisplayName("Strict h2 ALPN, h2 allowed") public class StrictH2AlpnH2Allowed extends H2AlpnTest { public StrictH2AlpnH2Allowed() throws Exception { super(true, true); } } @Nested @DisplayName("Strict h2 ALPN, h2 disallowed") public class StrictH2AlpnH2Disllowed extends H2AlpnTest { public StrictH2AlpnH2Disllowed() throws Exception { super(true, false); } } @Nested @DisplayName("Non-strict h2 ALPN, h2 allowed") public class NonStrictH2AlpnH2Disllowed extends H2AlpnTest { public NonStrictH2AlpnH2Disllowed() throws Exception { super(false, true); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/AsyncServerBootstrapFilterTest.java0100664 0000000 0000000 00000014313 14403631147 032567 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncFilterChain; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.UriPatternMatcher; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.testing.nio.extension.HttpAsyncRequesterResource; import org.apache.hc.core5.testing.nio.extension.HttpAsyncServerResource; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class AsyncServerBootstrapFilterTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private final HttpAsyncServerResource serverResource = new HttpAsyncServerResource(bootstrap -> bootstrap .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setLookupRegistry(new UriPatternMatcher<>()) .register("*", () -> new EchoHandler(2048)) .addFilterLast("test-filter", (request, entityDetails, context, responseTrigger, chain) -> chain.proceed(request, entityDetails, context, new AsyncFilterChain.ResponseTrigger() { @Override public void sendInformation( final HttpResponse response) throws HttpException, IOException { responseTrigger.sendInformation(response); } @Override public void submitResponse( final HttpResponse response, final AsyncEntityProducer entityProducer) throws HttpException, IOException { response.setHeader("X-Test-Filter", "active"); responseTrigger.submitResponse(response, entityProducer); } @Override public void pushPromise( final HttpRequest promise, final AsyncPushProducer responseProducer) throws HttpException, IOException { responseTrigger.pushPromise(promise, responseProducer); } }))); @RegisterExtension private final HttpAsyncRequesterResource clientResource = new HttpAsyncRequesterResource((bootstrap) -> bootstrap .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build())); @Test public void testFilters() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTP); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientResource.start(); final HttpHost target = new HttpHost("http", "localhost", address.getPort()); final Future> resultFuture = requester.execute( new BasicRequestProducer(Method.POST, target, "/filters", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message = resultFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message, CoreMatchers.notNullValue()); final HttpResponse response = message.getHead(); assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final Header testFilterHeader = response.getHeader("X-Test-Filter"); assertThat(testFilterHeader, CoreMatchers.notNullValue()); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/MultiLineResponseHandler.java0100664 0000000 0000000 00000006345 14245617503 031350 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncResponseBuilder; import org.apache.hc.core5.http.nio.support.BasicRequestConsumer; import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler; import org.apache.hc.core5.http.protocol.HttpContext; public class MultiLineResponseHandler extends BasicServerExchangeHandler> { public MultiLineResponseHandler(final String message, final int count) { super(new AsyncServerRequestHandler>() { @Override public AsyncRequestConsumer> prepare( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) throws HttpException { return new BasicRequestConsumer<>(entityDetails != null ? new StringAsyncEntityConsumer() : null); } @Override public void handle( final Message requestMessage, final ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { responseTrigger.submitResponse( AsyncResponseBuilder.create(HttpStatus.SC_OK) .setEntity(new MultiLineEntityProducer(message, count)) .build(), context); } } ); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/EchoHandler.java0100664 0000000 0000000 00000013402 14403631147 026571 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; public class EchoHandler implements AsyncServerExchangeHandler { private final static int MIN_CHUNK = 4096; private final int initBufferSize; private final AtomicInteger capacityIncrement; private volatile ByteBuffer buffer; private volatile CapacityChannel inputCapacityChannel; private volatile DataStreamChannel outputDataChannel; private volatile boolean endStream; public EchoHandler(final int bufferSize) { this.initBufferSize = bufferSize; this.capacityIncrement = new AtomicInteger(); this.buffer = ByteBuffer.allocate(bufferSize); } private void ensureCapacity(final int chunk) { if (buffer.remaining() < chunk) { final ByteBuffer oldBuffer = buffer; oldBuffer.flip(); final int newCapacity = oldBuffer.remaining() + Math.max(chunk, MIN_CHUNK); buffer = ByteBuffer.allocate(newCapacity); buffer.put(oldBuffer); } } private void signalCapacity() throws IOException { if (inputCapacityChannel != null) { if (capacityIncrement.get() > MIN_CHUNK) { final int n = capacityIncrement.getAndSet(0); if (n > 0) { inputCapacityChannel.update(n); } } } } @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); responseChannel.sendResponse(response, entityDetails, context); } @Override public void consume(final ByteBuffer src) throws IOException { if (buffer.position() == 0) { if (outputDataChannel != null) { final int bytesWritten = outputDataChannel.write(src); if (bytesWritten > 0) { capacityIncrement.addAndGet(bytesWritten); } } } if (src.hasRemaining()) { ensureCapacity(src.remaining()); buffer.put(src); if (outputDataChannel != null) { outputDataChannel.requestOutput(); } } signalCapacity(); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { if (buffer.hasRemaining()) { final int n = Math.min(initBufferSize, buffer.remaining()) + capacityIncrement.getAndSet(0); capacityChannel.update(n); } inputCapacityChannel = capacityChannel; } @Override public void streamEnd(final List trailers) throws HttpException, IOException { endStream = true; inputCapacityChannel = null; if (buffer.position() == 0) { if (outputDataChannel != null) { outputDataChannel.endStream(); } } else { if (outputDataChannel != null) { outputDataChannel.requestOutput(); } } } @Override public int available() { return buffer.position(); } @Override public void produce(final DataStreamChannel channel) throws IOException { outputDataChannel = channel; buffer.flip(); if (buffer.hasRemaining()) { final int bytesWritten = channel.write(buffer); if (bytesWritten > 0) { capacityIncrement.addAndGet(bytesWritten); } } buffer.compact(); if (buffer.position() == 0 && endStream) { channel.endStream(); } signalCapacity(); } @Override public void failed(final Exception cause) { } @Override public void releaseResources() { } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/MultiBinEntityProducer.java0100664 0000000 0000000 00000005615 14245617503 031054 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.nio.StreamChannel; import org.apache.hc.core5.http.nio.entity.AbstractBinAsyncEntityProducer; public class MultiBinEntityProducer extends AbstractBinAsyncEntityProducer { private final byte[] bin; private final int total; private final ByteBuffer bytebuf; private int count; public MultiBinEntityProducer(final byte[] bin, final int total, final ContentType contentType) { super(-1, contentType); this.bin = bin; this.total = total; this.bytebuf = ByteBuffer.allocate(4096); this.count = 0; } @Override public boolean isRepeatable() { return true; } @Override protected int availableData() { return Integer.MAX_VALUE; } @Override protected void produceData(final StreamChannel channel) throws IOException { while (bytebuf.remaining() > bin.length + 2 && count < total) { bytebuf.put(bin); count++; } if (bytebuf.position() > 0) { bytebuf.flip(); channel.write(bytebuf); bytebuf.compact(); } if (count >= total && bytebuf.position() == 0) { channel.endStream(); } } @Override public boolean isChunked() { return false; } @Override public long getContentLength() { return bin.length * total; } @Override public void failed(final Exception cause) { } @Override public void releaseResources() { count = 0; bytebuf.clear(); super.releaseResources(); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/H2ServerBootstrapFiltersTest.java0100664 0000000 0000000 00000014560 14403631147 032152 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncFilterChain; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.UriPatternMatcher; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.testing.nio.extension.H2AsyncRequesterResource; import org.apache.hc.core5.testing.nio.extension.H2AsyncServerResource; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class H2ServerBootstrapFiltersTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private final H2AsyncServerResource serverResource = new H2AsyncServerResource((bootstrap) -> bootstrap .setLookupRegistry(new UriPatternMatcher<>()) .setVersionPolicy(HttpVersionPolicy.NEGOTIATE) .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .register("*", () -> new EchoHandler(2048)) .addFilterLast("test-filter", (request, entityDetails, context, responseTrigger, chain) -> chain.proceed(request, entityDetails, context, new AsyncFilterChain.ResponseTrigger() { @Override public void sendInformation( final HttpResponse response) throws HttpException, IOException { responseTrigger.sendInformation(response); } @Override public void submitResponse( final HttpResponse response, final AsyncEntityProducer entityProducer) throws HttpException, IOException { response.setHeader("X-Test-Filter", "active"); responseTrigger.submitResponse(response, entityProducer); } @Override public void pushPromise( final HttpRequest promise, final AsyncPushProducer responseProducer) throws HttpException, IOException { responseTrigger.pushPromise(promise, responseProducer); } }))); @RegisterExtension private final H2AsyncRequesterResource clientResource = new H2AsyncRequesterResource(bootstrap -> bootstrap .setVersionPolicy(HttpVersionPolicy.NEGOTIATE) .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build())); @Test public void testSequentialRequests() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTP); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientResource.start(); final HttpHost target = new HttpHost("http", "localhost", address.getPort()); final Future> resultFuture = requester.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message = resultFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message, CoreMatchers.notNullValue()); final HttpResponse response = message.getHead(); assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final Header testFilterHeader = response.getHeader("X-Test-Filter"); assertThat(testFilterHeader, CoreMatchers.notNullValue()); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/MultiLineEntityProducer.java0100664 0000000 0000000 00000005750 14245617503 031233 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.nio.StreamChannel; import org.apache.hc.core5.http.nio.entity.AbstractCharAsyncEntityProducer; public class MultiLineEntityProducer extends AbstractCharAsyncEntityProducer { private final String text; private final int total; private final CharBuffer charbuf; private int count; public MultiLineEntityProducer(final String text, final int total, final Charset charset) { super(1024, -1, ContentType.TEXT_PLAIN.withCharset(charset)); this.text = text; this.total = total; this.charbuf = CharBuffer.allocate(4096); this.count = 0; } public MultiLineEntityProducer(final String text, final int total) { this(text, total, StandardCharsets.US_ASCII); } @Override public boolean isRepeatable() { return true; } @Override protected int availableData() { return Integer.MAX_VALUE; } @Override protected void produceData(final StreamChannel channel) throws IOException { while (charbuf.remaining() > text.length() + 2 && count < total) { charbuf.put(text + "\r\n"); count++; } if (charbuf.position() > 0) { charbuf.flip(); channel.write(charbuf); charbuf.compact(); } if (count >= total && charbuf.position() == 0) { channel.endStream(); } } @Override public void failed(final Exception cause) { } @Override public void releaseResources() { count = 0; charbuf.clear(); super.releaseResources(); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1AuthenticationTest.java0100664 0000000 0000000 00000031003 14403631147 031152 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import static org.hamcrest.MatcherAssert.assertThat; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.util.Random; import java.util.concurrent.Future; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.impl.bootstrap.StandardFilter; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AbstractAsyncServerAuthFilter; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.testing.nio.extension.HttpAsyncRequesterResource; import org.apache.hc.core5.testing.nio.extension.HttpAsyncServerResource; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class Http1AuthenticationTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private final HttpAsyncServerResource serverResource; @RegisterExtension private final HttpAsyncRequesterResource clientResource; public Http1AuthenticationTest(final boolean respondImmediately) { this.serverResource = new HttpAsyncServerResource(bootstrap -> bootstrap .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setLookupRegistry(null) // same as the default .register("*", () -> new EchoHandler(2048)) .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), new AbstractAsyncServerAuthFilter(respondImmediately) { @Override protected String parseChallengeResponse( final String challenge, final HttpContext context) throws HttpException { return challenge; } @Override protected boolean authenticate( final String challengeResponse, final URIAuthority authority, final String requestUri, final HttpContext context) { return challengeResponse != null && challengeResponse.equals("let me pass"); } @Override protected String generateChallenge( final String challengeResponse, final URIAuthority authority, final String requestUri, final HttpContext context) { return "who goes there?"; } @Override protected AsyncEntityProducer generateResponseContent(final HttpResponse unauthorized) { return AsyncEntityProducers.create("You shall not pass!!!"); } }) ); this.clientResource = new HttpAsyncRequesterResource(bootstrap -> bootstrap .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) ); } @Test public void testGetRequestAuthentication() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTP); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientResource.start(); final HttpHost target = new HttpHost("localhost", address.getPort()); final HttpRequest request1 = new BasicHttpRequest(Method.GET, target, "/stuff"); final Future> resultFuture1 = requester.execute( new BasicRequestProducer(request1, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED)); final String body1 = message1.getBody(); assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!")); final HttpRequest request2 = new BasicHttpRequest(Method.GET, target, "/stuff"); request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass"); final Future> resultFuture2 = requester.execute( new BasicRequestProducer(request2, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message2, CoreMatchers.notNullValue()); final HttpResponse response2 = message2.getHead(); assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = message2.getBody(); assertThat(body2, CoreMatchers.equalTo("")); } @Test public void testPostRequestAuthentication() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTP); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientResource.start(); final HttpHost target = new HttpHost("localhost", address.getPort()); final Random rnd = new Random(); final byte[] stuff = new byte[10240]; for (int i = 0; i < stuff.length; i++) { stuff[i] = (byte) ('a' + rnd.nextInt(10)); } final HttpRequest request1 = new BasicHttpRequest(Method.POST, target, "/stuff"); final Future> resultFuture1 = requester.execute( new BasicRequestProducer(request1, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED)); final String body1 = message1.getBody(); assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!")); final HttpRequest request2 = new BasicHttpRequest(Method.POST, target, "/stuff"); request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass"); final Future> resultFuture2 = requester.execute( new BasicRequestProducer(request2, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message2, CoreMatchers.notNullValue()); final HttpResponse response2 = message2.getHead(); assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = message2.getBody(); assertThat(body2, CoreMatchers.equalTo(new String(stuff, StandardCharsets.US_ASCII))); } @Test public void testPostRequestAuthenticationNoExpectContinue() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTP); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientResource.start(); final HttpHost target = new HttpHost("localhost", address.getPort()); final Random rnd = new Random(); final byte[] stuff = new byte[10240]; for (int i = 0; i < stuff.length; i++) { stuff[i] = (byte) ('a' + rnd.nextInt(10)); } final HttpRequest request1 = new BasicHttpRequest(Method.POST, target, "/stuff"); request1.setVersion(HttpVersion.HTTP_1_0); final Future> resultFuture1 = requester.execute( new BasicRequestProducer(request1, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_UNAUTHORIZED)); final String body1 = message1.getBody(); assertThat(body1, CoreMatchers.equalTo("You shall not pass!!!")); final HttpRequest request2 = new BasicHttpRequest(Method.POST, target, "/stuff"); request2.setVersion(HttpVersion.HTTP_1_0); request2.setHeader(HttpHeaders.AUTHORIZATION, "let me pass"); final Future> resultFuture2 = requester.execute( new BasicRequestProducer(request2, AsyncEntityProducers.create(stuff, ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message2, CoreMatchers.notNullValue()); final HttpResponse response2 = message2.getHead(); assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = message2.getBody(); assertThat(body2, CoreMatchers.equalTo(new String(stuff, StandardCharsets.US_ASCII))); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/H2AlpnTest.java0100664 0000000 0000000 00000014701 14403631147 026344 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.InetSocketAddress; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.ssl.BasicServerTlsStrategy; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http2.impl.nio.ProtocolNegotiationException; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2MultiplexingRequester; import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy; import org.apache.hc.core5.http2.ssl.H2ServerTlsStrategy; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.nio.extension.H2AsyncServerResource; import org.apache.hc.core5.testing.nio.extension.H2MultiplexingRequesterResource; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class H2AlpnTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); private final boolean strictALPN; private final boolean h2Allowed; @RegisterExtension private final H2AsyncServerResource serverResource; @RegisterExtension private final H2MultiplexingRequesterResource clientResource; public H2AlpnTest(final boolean strictALPN, final boolean h2Allowed) throws Exception { this.strictALPN = strictALPN; this.h2Allowed = h2Allowed; final TlsStrategy serverTlsStrategy = h2Allowed ? new H2ServerTlsStrategy(SSLTestContexts.createServerSSLContext()) : new BasicServerTlsStrategy(SSLTestContexts.createServerSSLContext()); this.serverResource = new H2AsyncServerResource(bootstrap -> bootstrap .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setTlsStrategy(serverTlsStrategy) .register("*", () -> new EchoHandler(2048)) ); final TlsStrategy clientTlsStrategy = new H2ClientTlsStrategy(SSLTestContexts.createClientSSLContext()); this.clientResource = new H2MultiplexingRequesterResource(bootstrap -> bootstrap .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setTlsStrategy(clientTlsStrategy) .setStrictALPNHandshake(strictALPN) ); } @Test public void testALPN() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTPS); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final H2MultiplexingRequester requester = clientResource.start(); final HttpHost target = new HttpHost(URIScheme.HTTPS.id, "localhost", address.getPort()); final Future> resultFuture1 = requester.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message1; try { message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); } catch (final ExecutionException e) { final Throwable cause = e.getCause(); assertFalse(h2Allowed, "h2 negotiation was enabled, but h2 was not negotiated"); assertTrue(cause instanceof ProtocolNegotiationException); assertEquals("ALPN: missing application protocol", cause.getMessage()); assertTrue(strictALPN, "strict ALPN mode was not enabled, but the client negotiator still threw"); return; } assertTrue(h2Allowed, "h2 negotiation was disabled, but h2 was negotiated"); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = message1.getBody(); assertThat(body1, CoreMatchers.equalTo("some stuff")); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/H2ConnPoolTest.java0100664 0000000 0000000 00000015320 14442305417 027200 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.net.InetSocketAddress; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2MultiplexingRequester; import org.apache.hc.core5.http2.nio.command.PingCommand; import org.apache.hc.core5.http2.nio.pool.H2ConnPool; import org.apache.hc.core5.http2.nio.support.BasicPingHandler; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.testing.nio.extension.H2AsyncServerResource; import org.apache.hc.core5.testing.nio.extension.H2MultiplexingRequesterResource; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public class H2ConnPoolTest { private static final Timeout TIMEOUT = Timeout.ofSeconds(30); private final AtomicLong clientConnCount; @RegisterExtension private final H2AsyncServerResource serverResource; @RegisterExtension private final H2MultiplexingRequesterResource clientResource; public H2ConnPoolTest() throws Exception { this.serverResource = new H2AsyncServerResource(bootstrap -> bootstrap .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2) .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .register("*", () -> new EchoHandler(2048)) ); this.clientConnCount = new AtomicLong(); this.clientResource = new H2MultiplexingRequesterResource(bootstrap -> bootstrap .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setIOSessionListener(new LoggingIOSessionListener() { @Override public void connected(final IOSession session) { clientConnCount.incrementAndGet(); super.connected(session); } }) ); } @BeforeEach public void resetCounts() { clientConnCount.set(0); } @Test public void testManyGetSession() throws Exception { final int n = 200; final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTP); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpHost target = new HttpHost(URIScheme.HTTP.id, "localhost", address.getPort()); final H2MultiplexingRequester requester = clientResource.start(); final H2ConnPool connPool = requester.getConnPool(); final CountDownLatch latch = new CountDownLatch(n); for (int i = 0; i < n; i++) { connPool.getSession(target, TIMEOUT, new FutureCallback() { @Override public void completed(final IOSession session) { session.enqueue(new PingCommand(new BasicPingHandler( result -> { latch.countDown(); })), Command.Priority.IMMEDIATE); } @Override public void failed(final Exception ex) { latch.countDown(); } @Override public void cancelled() { latch.countDown(); } }); } Assertions.assertTrue(latch.await(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); requester.initiateShutdown(); requester.awaitShutdown(TIMEOUT); Assertions.assertEquals(1, clientConnCount.get()); } @Test public void testManyGetSessionFailures() throws Exception { final int n = 200; final HttpHost target = new HttpHost(URIScheme.HTTP.id, "pampa.invalid", 8888); final H2MultiplexingRequester requester = clientResource.start(); final H2ConnPool connPool = requester.getConnPool(); final CountDownLatch latch = new CountDownLatch(n); final ConcurrentLinkedQueue concurrentConnections = new ConcurrentLinkedQueue<>(); for (int i = 0; i < n; i++) { connPool.getSession(target, TIMEOUT, new FutureCallback() { @Override public void completed(final IOSession session) { latch.countDown(); } @Override public void failed(final Exception ex) { latch.countDown(); } @Override public void cancelled() { latch.countDown(); } }); } requester.initiateShutdown(); requester.awaitShutdown(TIMEOUT); Assertions.assertEquals(0, clientConnCount.get()); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/MessageExchangeHandler.java0100664 0000000 0000000 00000005277 14245617503 030761 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.support.BasicRequestConsumer; import org.apache.hc.core5.http.nio.support.AbstractServerExchangeHandler; import org.apache.hc.core5.http.protocol.HttpContext; /** * @since 5.0 */ public abstract class MessageExchangeHandler extends AbstractServerExchangeHandler> { private final AsyncRequestConsumer> requestConsumer; public MessageExchangeHandler(final AsyncRequestConsumer> requestConsumer) { super(); this.requestConsumer = requestConsumer; } public MessageExchangeHandler(final Supplier> dataConsumerSupplier) { this(new BasicRequestConsumer<>(dataConsumerSupplier)); } public MessageExchangeHandler(final AsyncEntityConsumer entityConsumer) { this(new BasicRequestConsumer<>(entityConsumer)); } @Override protected AsyncRequestConsumer> supplyConsumer( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) throws HttpException { return requestConsumer; } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/0040775 0000000 0000000 00000000000 14403631147 025571 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/Http1TestResources.java0100664 0000000 0000000 00000007165 14403631147 032175 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio.extension; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.nio.Http1TestClient; import org.apache.hc.core5.testing.nio.Http1TestServer; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Http1TestResources implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(Http1TestResources.class); private final URIScheme scheme; private final Timeout socketTimeout; private Http1TestServer server; private Http1TestClient client; public Http1TestResources(final URIScheme scheme, final Timeout socketTimeout) { this.scheme = scheme; this.socketTimeout = socketTimeout; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test server"); server = new Http1TestServer( IOReactorConfig.custom() .setSoTimeout(socketTimeout) .build(), scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null, null, null); LOG.debug("Starting up test client"); client = new Http1TestClient( IOReactorConfig.custom() .setSoTimeout(socketTimeout) .build(), scheme == URIScheme.HTTPS ? SSLTestContexts.createClientSSLContext() : null, null, null); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test client"); if (client != null) { client.shutdown(TimeValue.ofSeconds(5)); } LOG.debug("Shutting down test server"); if (server != null) { server.shutdown(TimeValue.ofSeconds(5)); } } public Http1TestClient client() { Assertions.assertNotNull(client); return client; } public Http1TestServer server() { Assertions.assertNotNull(server); return server; } } ././@LongLink0100644 0000000 0000000 00000000145 14403631147 011636 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/H2AsyncServerResource.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/H2AsyncServerResource.java0100664 0000000 0000000 00000007457 14403631147 032614 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio.extension; import java.io.IOException; import java.util.function.Consumer; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2ServerBootstrap; import org.apache.hc.core5.http2.ssl.H2ServerTlsStrategy; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.nio.LoggingExceptionCallback; import org.apache.hc.core5.testing.nio.LoggingH2StreamListener; import org.apache.hc.core5.testing.nio.LoggingHttp1StreamListener; import org.apache.hc.core5.testing.nio.LoggingIOSessionDecorator; import org.apache.hc.core5.testing.nio.LoggingIOSessionListener; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class H2AsyncServerResource implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(H2AsyncServerResource.class); private final Consumer bootstrapCustomizer; private HttpAsyncServer server; public H2AsyncServerResource(final Consumer bootstrapCustomizer) { this.bootstrapCustomizer = bootstrapCustomizer; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test server"); final H2ServerBootstrap bootstrap = H2ServerBootstrap.bootstrap() .setTlsStrategy(new H2ServerTlsStrategy(SSLTestContexts.createServerSSLContext())) .setStreamListener(LoggingHttp1StreamListener.INSTANCE_SERVER) .setStreamListener(LoggingH2StreamListener.INSTANCE) .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE) .setExceptionCallback(LoggingExceptionCallback.INSTANCE) .setIOSessionListener(LoggingIOSessionListener.INSTANCE); bootstrapCustomizer.accept(bootstrap); server = bootstrap.create(); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test server"); if (server != null) { try { server.close(CloseMode.IMMEDIATE); } catch (final Exception ignore) { } } } public HttpAsyncServer start() throws IOException { Assertions.assertNotNull(server); server.start(); return server; } } ././@LongLink0100644 0000000 0000000 00000000152 14403631147 011634 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/HttpAsyncRequesterResource.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/HttpAsyncRequesterResource0100664 0000000 0000000 00000007423 14403631147 033044 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio.extension; import java.util.function.Consumer; import org.apache.hc.core5.http.impl.bootstrap.AsyncRequesterBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.nio.ssl.BasicClientTlsStrategy; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.classic.LoggingConnPoolListener; import org.apache.hc.core5.testing.nio.LoggingHttp1StreamListener; import org.apache.hc.core5.testing.nio.LoggingIOSessionDecorator; import org.apache.hc.core5.testing.nio.LoggingIOSessionListener; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HttpAsyncRequesterResource implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(HttpAsyncRequesterResource.class); private final Consumer bootstrapCustomizer; private HttpAsyncRequester requester; public HttpAsyncRequesterResource(final Consumer bootstrapCustomizer) { this.bootstrapCustomizer = bootstrapCustomizer; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test client"); final AsyncRequesterBootstrap bootstrap = AsyncRequesterBootstrap.bootstrap() .setTlsStrategy(new BasicClientTlsStrategy(SSLTestContexts.createClientSSLContext())) .setMaxTotal(2) .setDefaultMaxPerRoute(2) .setIOSessionListener(LoggingIOSessionListener.INSTANCE) .setStreamListener(LoggingHttp1StreamListener.INSTANCE_CLIENT) .setConnPoolListener(LoggingConnPoolListener.INSTANCE) .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE); bootstrapCustomizer.accept(bootstrap); requester = bootstrap.create(); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test client"); if (requester != null) { try { requester.close(CloseMode.GRACEFUL); } catch (final Exception ignore) { } } } public HttpAsyncRequester start() { Assertions.assertNotNull(requester); requester.start(); return requester; } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/H2TestResources.java0100664 0000000 0000000 00000007124 14403631147 031441 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio.extension; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.nio.H2TestClient; import org.apache.hc.core5.testing.nio.H2TestServer; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class H2TestResources implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(H2TestResources.class); private final URIScheme scheme; private final Timeout socketTimeout; private H2TestServer server; private H2TestClient client; public H2TestResources(final URIScheme scheme, final Timeout socketTimeout) { this.scheme = scheme; this.socketTimeout = socketTimeout; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test server"); server = new H2TestServer( IOReactorConfig.custom() .setSoTimeout(socketTimeout) .build(), scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null, null, null); LOG.debug("Starting up test client"); client = new H2TestClient( IOReactorConfig.custom() .setSoTimeout(socketTimeout) .build(), scheme == URIScheme.HTTPS ? SSLTestContexts.createClientSSLContext() : null, null, null); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test client"); if (client != null) { client.shutdown(TimeValue.ofSeconds(5)); } LOG.debug("Shutting down test server"); if (server != null) { server.shutdown(TimeValue.ofSeconds(5)); } } public H2TestClient client() { Assertions.assertNotNull(client); return client; } public H2TestServer server() { Assertions.assertNotNull(server); return server; } } ././@LongLink0100644 0000000 0000000 00000000157 14403631147 011641 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/H2MultiplexingRequesterResource.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/H2MultiplexingRequesterRes0100664 0000000 0000000 00000007407 14403631147 032746 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio.extension; import java.util.function.Consumer; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2MultiplexingRequester; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2MultiplexingRequesterBootstrap; import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.nio.LoggingExceptionCallback; import org.apache.hc.core5.testing.nio.LoggingH2StreamListener; import org.apache.hc.core5.testing.nio.LoggingIOSessionDecorator; import org.apache.hc.core5.testing.nio.LoggingIOSessionListener; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class H2MultiplexingRequesterResource implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(H2MultiplexingRequesterResource.class); private final Consumer bootstrapCustomizer; private H2MultiplexingRequester requester; public H2MultiplexingRequesterResource(final Consumer bootstrapCustomizer) { this.bootstrapCustomizer = bootstrapCustomizer; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test client"); final H2MultiplexingRequesterBootstrap bootstrap = H2MultiplexingRequesterBootstrap.bootstrap() .setTlsStrategy(new H2ClientTlsStrategy(SSLTestContexts.createClientSSLContext())) .setStreamListener(LoggingH2StreamListener.INSTANCE) .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE) .setExceptionCallback(LoggingExceptionCallback.INSTANCE) .setIOSessionListener(LoggingIOSessionListener.INSTANCE); bootstrapCustomizer.accept(bootstrap); requester = bootstrap.create(); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test client"); if (requester != null) { try { requester.close(CloseMode.GRACEFUL); } catch (final Exception ignore) { } } } public H2MultiplexingRequester start() { Assertions.assertNotNull(requester); requester.start(); return requester; } } ././@LongLink0100644 0000000 0000000 00000000150 14403631147 011632 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/H2AsyncRequesterResource.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/H2AsyncRequesterResource.j0100664 0000000 0000000 00000007677 14403631147 032641 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio.extension; import java.util.function.Consumer; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap; import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.classic.LoggingConnPoolListener; import org.apache.hc.core5.testing.nio.LoggingExceptionCallback; import org.apache.hc.core5.testing.nio.LoggingH2StreamListener; import org.apache.hc.core5.testing.nio.LoggingHttp1StreamListener; import org.apache.hc.core5.testing.nio.LoggingIOSessionDecorator; import org.apache.hc.core5.testing.nio.LoggingIOSessionListener; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class H2AsyncRequesterResource implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(H2AsyncRequesterResource.class); private final Consumer bootstrapCustomizer; private HttpAsyncRequester requester; public H2AsyncRequesterResource(final Consumer bootstrapCustomizer) { this.bootstrapCustomizer = bootstrapCustomizer; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test client"); final H2RequesterBootstrap bootstrap = H2RequesterBootstrap.bootstrap() .setTlsStrategy(new H2ClientTlsStrategy(SSLTestContexts.createClientSSLContext())) .setStreamListener(LoggingHttp1StreamListener.INSTANCE_CLIENT) .setStreamListener(LoggingH2StreamListener.INSTANCE) .setConnPoolListener(LoggingConnPoolListener.INSTANCE) .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE) .setExceptionCallback(LoggingExceptionCallback.INSTANCE) .setIOSessionListener(LoggingIOSessionListener.INSTANCE); bootstrapCustomizer.accept(bootstrap); requester = bootstrap.create(); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test client"); if (requester != null) { try { requester.close(CloseMode.GRACEFUL); } catch (final Exception ignore) { } } } public HttpAsyncRequester start() { Assertions.assertNotNull(requester); requester.start(); return requester; } } ././@LongLink0100644 0000000 0000000 00000000147 14403631147 011640 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/HttpAsyncServerResource.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/extension/HttpAsyncServerResource.ja0100664 0000000 0000000 00000007303 14403631147 032721 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio.extension; import java.io.IOException; import java.util.function.Consumer; import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.ssl.BasicServerTlsStrategy; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.testing.SSLTestContexts; import org.apache.hc.core5.testing.nio.LoggingExceptionCallback; import org.apache.hc.core5.testing.nio.LoggingHttp1StreamListener; import org.apache.hc.core5.testing.nio.LoggingIOSessionDecorator; import org.apache.hc.core5.testing.nio.LoggingIOSessionListener; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HttpAsyncServerResource implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(HttpAsyncServerResource.class); private final Consumer bootstrapCustomizer; private HttpAsyncServer server; public HttpAsyncServerResource(final Consumer bootstrapCustomizer) { this.bootstrapCustomizer = bootstrapCustomizer; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test server"); final AsyncServerBootstrap bootstrap = AsyncServerBootstrap.bootstrap() .setTlsStrategy(new BasicServerTlsStrategy(SSLTestContexts.createServerSSLContext())) .setStreamListener(LoggingHttp1StreamListener.INSTANCE_SERVER) .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE) .setExceptionCallback(LoggingExceptionCallback.INSTANCE) .setIOSessionListener(LoggingIOSessionListener.INSTANCE); bootstrapCustomizer.accept(bootstrap); server = bootstrap.create(); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test server"); if (server != null) { try { server.close(CloseMode.IMMEDIATE); } catch (final Exception ignore) { } } } public HttpAsyncServer start() throws IOException { Assertions.assertNotNull(server); server.start(); return server; } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/JSSEProviderIntegrationTests.java0100664 0000000 0000000 00000003705 14403631147 032130 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; public class JSSEProviderIntegrationTests { @Nested @DisplayName("Oracle (default)") public class Oracle extends JSSEProviderIntegrationTest { public Oracle() { super("Oracle", null); } } @Nested @DisplayName("Conscrypt (TLSv1.2)") public class ConscryptTlsV1_2 extends JSSEProviderIntegrationTest { public ConscryptTlsV1_2() { super("Conscrypt", "TLSv1.2"); } } @Nested @DisplayName("Conscrypt (TLSv1.3)") public class ConscryptTlsV1_3 extends JSSEProviderIntegrationTest { public ConscryptTlsV1_3() { super("Conscrypt", "TLSv1.3"); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/NoopIOEventHandlerFactory.java0100664 0000000 0000000 00000004547 14403631147 031422 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.nio.ByteBuffer; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Timeout; class NoopIOEventHandlerFactory implements IOEventHandlerFactory { @Override public IOEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { return new IOEventHandler() { @Override public void connected(final IOSession session) { } @Override public void inputReady(final IOSession session, final ByteBuffer src) { } @Override public void outputReady(final IOSession session) { } @Override public void timeout(final IOSession session, final Timeout timeout) { } @Override public void exception(final IOSession session, final Exception cause) { } @Override public void disconnected(final IOSession session) { } }; } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/H2ProtocolNegotiationTest.java0100664 0000000 0000000 00000020071 14403631147 031451 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import static org.hamcrest.MatcherAssert.assertThat; import java.net.InetSocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.testing.nio.extension.H2AsyncRequesterResource; import org.apache.hc.core5.testing.nio.extension.H2AsyncServerResource; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public class H2ProtocolNegotiationTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private final H2AsyncServerResource serverResource; @RegisterExtension private final H2AsyncRequesterResource clientResource; public H2ProtocolNegotiationTest() { this.serverResource = new H2AsyncServerResource(bootstrap -> bootstrap .setVersionPolicy(HttpVersionPolicy.NEGOTIATE) .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .register("*", () -> new EchoHandler(2048)) ); this.clientResource = new H2AsyncRequesterResource(bootstrap -> bootstrap .setVersionPolicy(HttpVersionPolicy.NEGOTIATE) .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) ); } @Test public void testForceHttp1() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTPS); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientResource.start(); final HttpHost target = new HttpHost(URIScheme.HTTPS.id, "localhost", address.getPort()); final Future connectFuture = requester.connect(target, TIMEOUT, HttpVersionPolicy.FORCE_HTTP_1, null); final AsyncClientEndpoint endpoint = connectFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final Future> resultFuture1 = endpoint.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); assertThat(response1.getVersion(), CoreMatchers.equalTo(HttpVersion.HTTP_1_1)); } @Test public void testForceHttp2() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTPS); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientResource.start(); final HttpHost target = new HttpHost(URIScheme.HTTPS.id, "localhost", address.getPort()); final Future connectFuture = requester.connect(target, TIMEOUT, HttpVersionPolicy.FORCE_HTTP_2, null); final AsyncClientEndpoint endpoint = connectFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final Future> resultFuture1 = endpoint.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); assertThat(response1.getVersion(), CoreMatchers.equalTo(HttpVersion.HTTP_2)); } @Test public void testNegotiateProtocol() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTPS); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientResource.start(); final HttpHost target = new HttpHost(URIScheme.HTTPS.id, "localhost", address.getPort()); final Future connectFuture = requester.connect(target, TIMEOUT, HttpVersionPolicy.NEGOTIATE, null); final AsyncClientEndpoint endpoint = connectFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final Future> resultFuture1 = endpoint.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); assertThat(response1.getVersion(), CoreMatchers.equalTo(HttpVersion.HTTP_2)); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/H2IntegrationTest.java0100664 0000000 0000000 00000141607 14403631147 027743 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import static org.hamcrest.MatcherAssert.assertThat; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.StringTokenizer; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncResponseProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.nio.entity.DigestingEntityConsumer; import org.apache.hc.core5.http.nio.entity.DigestingEntityProducer; import org.apache.hc.core5.http.nio.entity.DiscardingEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.AbstractAsyncPushHandler; import org.apache.hc.core5.http.nio.support.AbstractServerExchangeHandler; import org.apache.hc.core5.http.nio.support.AsyncResponseBuilder; import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator; import org.apache.hc.core5.http.nio.support.BasicPushProducer; import org.apache.hc.core5.http.nio.support.BasicRequestConsumer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.nio.support.BasicResponseProducer; import org.apache.hc.core5.http.nio.support.classic.AbstractClassicEntityConsumer; import org.apache.hc.core5.http.nio.support.classic.AbstractClassicEntityProducer; import org.apache.hc.core5.http.nio.support.classic.AbstractClassicServerExchangeHandler; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.H2StreamResetException; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.nio.command.PingCommand; import org.apache.hc.core5.http2.nio.support.BasicPingHandler; import org.apache.hc.core5.http2.protocol.H2RequestConnControl; import org.apache.hc.core5.http2.protocol.H2RequestContent; import org.apache.hc.core5.http2.protocol.H2RequestTargetHost; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.testing.nio.extension.H2TestResources; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class H2IntegrationTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); private static final Timeout LONG_TIMEOUT = Timeout.ofMinutes(2); private final URIScheme scheme; @RegisterExtension private final H2TestResources resources; public H2IntegrationTest(final URIScheme scheme) { this.scheme = scheme; this.resources = new H2TestResources(scheme, TIMEOUT); } private URI createRequestURI(final InetSocketAddress serverEndpoint, final String path) { try { return new URI(scheme.id, null, "localhost", serverEndpoint.getPort(), path, null, null); } catch (final URISyntaxException e) { throw new IllegalStateException(); } } @Test public void testSimpleGet() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Queue>> queue = new LinkedList<>(); for (int i = 0; i < 10; i++) { queue.add(streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); final String entity = result.getBody(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertEquals("Hi there", entity); } } @Test public void testSimpleHead() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); for (int i = 0; i < 5; i++) { final Future> future = streamEndpoint.execute( new BasicRequestProducer(Method.HEAD, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response1 = result.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertNull(result.getBody()); } } @Test public void testLargeGet() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("/", () -> new MultiLineResponseHandler("0123456789abcdef", 5000)); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/"), null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Future> future2 = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer(512)), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); final String s1 = result1.getBody(); Assertions.assertNotNull(s1); final StringTokenizer t1 = new StringTokenizer(s1, "\r\n"); while (t1.hasMoreTokens()) { Assertions.assertEquals("0123456789abcdef", t1.nextToken()); } final Message result2 = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result2); final HttpResponse response2 = result2.getHead(); Assertions.assertNotNull(response2); Assertions.assertEquals(200, response2.getCode()); final String s2 = result2.getBody(); Assertions.assertNotNull(s2); final StringTokenizer t2 = new StringTokenizer(s2, "\r\n"); while (t2.hasMoreTokens()) { Assertions.assertEquals("0123456789abcdef", t2.nextToken()); } } @Test public void testBasicPost() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi back")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Queue>> queue = new LinkedList<>(); for (int i = 0; i < 10; i++) { final HttpRequest request = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/hello")); queue.add(streamEndpoint.execute( new BasicRequestProducer(request, new StringAsyncEntityProducer("Hi there", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); final String entity1 = result.getBody(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); Assertions.assertEquals("Hi back", entity1); } } @Test public void testLargePost() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("*", () -> new EchoHandler(2048)); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(Method.POST, createRequestURI(serverEndpoint, "/echo"), new MultiLineEntityProducer("0123456789abcdef", 5000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); final String s1 = result1.getBody(); Assertions.assertNotNull(s1); final StringTokenizer t1 = new StringTokenizer(s1, "\r\n"); while (t1.hasMoreTokens()) { Assertions.assertEquals("0123456789abcdef", t1.nextToken()); } } @Test public void testSlowResponseConsumer() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("/", () -> new MultiLineResponseHandler("0123456789abcd", 3)); final InetSocketAddress serverEndpoint = server.start(); client.start(H2Config.custom().setInitialWindowSize(16).build()); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/"), null), new BasicResponseConsumer<>(new AbstractClassicEntityConsumer(16, Executors.newSingleThreadExecutor()) { @Override protected String consumeData( final ContentType contentType, final InputStream inputStream) throws IOException { final Charset charset = ContentType.getCharset(contentType, StandardCharsets.US_ASCII); final StringBuilder buffer = new StringBuilder(); try { final byte[] tmp = new byte[16]; int l; while ((l = inputStream.read(tmp)) != -1) { buffer.append(charset.decode(ByteBuffer.wrap(tmp, 0, l))); Thread.sleep(500); } } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); throw new InterruptedIOException(ex.getMessage()); } return buffer.toString(); } }), null); final Message result1 = future1.get(LONG_TIMEOUT.getDuration(), LONG_TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); final String s1 = result1.getBody(); Assertions.assertNotNull(s1); final StringTokenizer t1 = new StringTokenizer(s1, "\r\n"); while (t1.hasMoreTokens()) { Assertions.assertEquals("0123456789abcd", t1.nextToken()); } } @Test public void testSlowRequestProducer() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("*", () -> new EchoHandler(2048)); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, new AbstractClassicEntityProducer(4096, ContentType.TEXT_PLAIN, Executors.newSingleThreadExecutor()) { @Override protected void produceData(final ContentType contentType, final OutputStream outputStream) throws IOException { final Charset charset = ContentType.getCharset(contentType, StandardCharsets.US_ASCII); try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, charset))) { for (int i = 0; i < 500; i++) { if (i % 100 == 0) { writer.flush(); Thread.sleep(500); } writer.write("0123456789abcdef\r\n"); } } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); throw new InterruptedIOException(ex.getMessage()); } } }), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(LONG_TIMEOUT.getDuration(), LONG_TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); final String s1 = result1.getBody(); Assertions.assertNotNull(s1); final StringTokenizer t1 = new StringTokenizer(s1, "\r\n"); while (t1.hasMoreTokens()) { Assertions.assertEquals("0123456789abcdef", t1.nextToken()); } } @Test public void testSlowResponseProducer() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("*", () -> new AbstractClassicServerExchangeHandler(2048, Executors.newSingleThreadExecutor()) { @Override protected void handle( final HttpRequest request, final InputStream requestStream, final HttpResponse response, final OutputStream responseStream, final HttpContext context) throws IOException, HttpException { if (!"/hello".equals(request.getPath())) { response.setCode(HttpStatus.SC_NOT_FOUND); return; } if (!Method.POST.name().equalsIgnoreCase(request.getMethod())) { response.setCode(HttpStatus.SC_NOT_IMPLEMENTED); return; } if (requestStream == null) { return; } final Header h1 = request.getFirstHeader(HttpHeaders.CONTENT_TYPE); final ContentType contentType = h1 != null ? ContentType.parse(h1.getValue()) : null; final Charset charset = ContentType.getCharset(contentType, StandardCharsets.US_ASCII); response.setCode(HttpStatus.SC_OK); response.setHeader(h1); try (final BufferedReader reader = new BufferedReader(new InputStreamReader(requestStream, charset)); final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(responseStream, charset))) { try { String l; int count = 0; while ((l = reader.readLine()) != null) { writer.write(l); writer.write("\r\n"); count++; if (count % 500 == 0) { Thread.sleep(500); } } writer.flush(); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); throw new InterruptedIOException(ex.getMessage()); } } } }); final InetSocketAddress serverEndpoint = server.start(); client.start(H2Config.custom() .setInitialWindowSize(512) .build()); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/hello")); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, new MultiLineEntityProducer("0123456789abcd", 2000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(LONG_TIMEOUT.getDuration(), LONG_TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); final String s1 = result1.getBody(); Assertions.assertNotNull(s1); final StringTokenizer t1 = new StringTokenizer(s1, "\r\n"); while (t1.hasMoreTokens()) { Assertions.assertEquals("0123456789abcd", t1.nextToken()); } } @Test public void testPush() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); final InetSocketAddress serverEndpoint = server.start(); server.register("/hello", () -> new MessageExchangeHandler(new DiscardingEntityConsumer<>()) { @Override protected void handle( final Message request, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws IOException, HttpException { responseTrigger.pushPromise( new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/stuff")), context, new BasicPushProducer(new MultiLineEntityProducer("Pushing lots of stuff", 500))); responseTrigger.submitResponse( AsyncResponseBuilder.create(HttpStatus.SC_OK).setEntity("Hi there", ContentType.TEXT_PLAIN).build(), context); } }); client.start(H2Config.custom().setPushEnabled(true).build()); final BlockingQueue> pushMessageQueue = new LinkedBlockingDeque<>(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), (request, context) -> new AbstractAsyncPushHandler>(new BasicResponseConsumer<>(new StringAsyncEntityConsumer())) { @Override protected void handleResponse( final HttpRequest promise, final Message responseMessage) throws IOException, HttpException { try { pushMessageQueue.put(responseMessage); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); throw new InterruptedIOException(ex.getMessage()); } } }, null, null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); final String entity1 = result1.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi there", entity1); final Message result2 = pushMessageQueue.poll(5, TimeUnit.SECONDS); Assertions.assertNotNull(result2); final HttpResponse response2 = result2.getHead(); final String entity2 = result2.getBody(); Assertions.assertEquals(200, response2.getCode()); Assertions.assertNotNull(entity2); final StringTokenizer t1 = new StringTokenizer(entity2, "\r\n"); while (t1.hasMoreTokens()) { Assertions.assertEquals("Pushing lots of stuff", t1.nextToken()); } } @Test public void testPushRefused() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); final BlockingQueue pushResultQueue = new LinkedBlockingDeque<>(); final InetSocketAddress serverEndpoint = server.start(); server.register("/hello", new Supplier() { @Override public AsyncServerExchangeHandler get() { return new MessageExchangeHandler(new DiscardingEntityConsumer<>()) { @Override protected void handle( final Message request, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws IOException, HttpException { responseTrigger.pushPromise( new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/stuff")), context, new BasicPushProducer(AsyncEntityProducers.create("Pushing all sorts of stuff")) { @Override public void failed(final Exception cause) { pushResultQueue.add(cause); super.failed(cause); } }); responseTrigger.pushPromise( new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/more-stuff")), context, new BasicPushProducer(new MultiLineEntityProducer("Pushing lots of stuff", 500)) { @Override public void failed(final Exception cause) { pushResultQueue.add(cause); super.failed(cause); } }); responseTrigger.submitResponse( new BasicResponseProducer(HttpStatus.SC_OK, AsyncEntityProducers.create("Hi there")), context); } }; } }); client.start(H2Config.custom().setPushEnabled(true).build()); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); final String entity1 = result1.getBody(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hi there", entity1); final Object result2 = pushResultQueue.poll(5, TimeUnit.SECONDS); Assertions.assertNotNull(result2); Assertions.assertTrue(result2 instanceof H2StreamResetException); Assertions.assertEquals(H2Error.REFUSED_STREAM.getCode(), ((H2StreamResetException) result2).getCode()); final Object result3 = pushResultQueue.poll(5, TimeUnit.SECONDS); Assertions.assertNotNull(result3); Assertions.assertTrue(result3 instanceof H2StreamResetException); Assertions.assertEquals(H2Error.REFUSED_STREAM.getCode(), ((H2StreamResetException) result3).getCode()); } @Test public void testExcessOfConcurrentStreams() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("/", () -> new MultiLineResponseHandler("0123456789abcdef", 2000)); final InetSocketAddress serverEndpoint = server.start(H2Config.custom().setMaxConcurrentStreams(20).build()); client.start(H2Config.custom().setMaxConcurrentStreams(20).build()); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final Queue>> queue = new LinkedList<>(); for (int i = 0; i < 2000; i++) { final HttpRequest request1 = new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/")); final Future> future = streamEndpoint.execute( new BasicRequestProducer(request1, null), new BasicResponseConsumer<>(new DiscardingEntityConsumer<>()), null); queue.add(future); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result); final HttpResponse response = result.getHead(); Assertions.assertNotNull(response); Assertions.assertEquals(200, response.getCode()); } } @Test public void testExpectationFailed() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("*", () -> new MessageExchangeHandler(new StringAsyncEntityConsumer()) { @Override protected void handle( final Message request, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws IOException, HttpException { responseTrigger.submitResponse(new BasicResponseProducer(HttpStatus.SC_OK, "All is well"), context); } }); final InetSocketAddress serverEndpoint = server.start(null, handler -> new BasicAsyncServerExpectationDecorator(handler) { @Override protected AsyncResponseProducer verify(final HttpRequest request, final HttpContext context) throws IOException, HttpException { final Header h = request.getFirstHeader("password"); if (h != null && "secret".equals(h.getValue())) { return null; } else { return new BasicResponseProducer(HttpStatus.SC_UNAUTHORIZED, "You shall not pass"); } } }, H2Config.DEFAULT); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); request1.addHeader("password", "secret"); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, new MultiLineEntityProducer("0123456789abcdef", 5000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertNotNull("All is well", result1.getBody()); final HttpRequest request2 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); final Future> future2 = streamEndpoint.execute( new BasicRequestProducer(request2, new MultiLineEntityProducer("0123456789abcdef", 5000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result2 = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result2); final HttpResponse response2 = result2.getHead(); Assertions.assertNotNull(response2); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response2.getCode()); Assertions.assertNotNull("You shall not pass", result2.getBody()); } @Test public void testPrematureResponse() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("*", new Supplier() { @Override public AsyncServerExchangeHandler get() { return new AsyncServerExchangeHandler() { private final AtomicReference responseProducer = new AtomicReference<>(); @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(Integer.MAX_VALUE); } @Override public void consume(final ByteBuffer src) throws IOException { } @Override public void streamEnd(final List trailers) throws HttpException, IOException { } @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final AsyncResponseProducer producer; final Header h = request.getFirstHeader("password"); if (h != null && "secret".equals(h.getValue())) { producer = new BasicResponseProducer(HttpStatus.SC_OK, "All is well"); } else { producer = new BasicResponseProducer(HttpStatus.SC_UNAUTHORIZED, "You shall not pass"); } responseProducer.set(producer); producer.sendResponse(responseChannel, context); } @Override public int available() { final AsyncResponseProducer producer = this.responseProducer.get(); return producer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { final AsyncResponseProducer producer = this.responseProducer.get(); producer.produce(channel); } @Override public void failed(final Exception cause) { } @Override public void releaseResources() { } }; } }); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo")); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, new MultiLineEntityProducer("0123456789abcdef", 5000)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response1.getCode()); Assertions.assertNotNull("You shall not pass", result1.getBody()); } @Test public void testMessageWithTrailers() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("/hello", () -> new AbstractServerExchangeHandler>() { @Override protected AsyncRequestConsumer> supplyConsumer( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) throws HttpException { return new BasicRequestConsumer<>(entityDetails != null ? new StringAsyncEntityConsumer() : null); } @Override protected void handle( final Message requestMessage, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { responseTrigger.submitResponse(new BasicResponseProducer( HttpStatus.SC_OK, new DigestingEntityProducer("MD5", new StringAsyncEntityProducer("Hello back with some trailers"))), context); } }); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/hello")); final DigestingEntityConsumer entityConsumer = new DigestingEntityConsumer<>("MD5", new StringAsyncEntityConsumer()); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, null), new BasicResponseConsumer<>(entityConsumer), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); Assertions.assertEquals("Hello back with some trailers", result1.getBody()); final List
trailers = entityConsumer.getTrailers(); Assertions.assertNotNull(trailers); Assertions.assertEquals(2, trailers.size()); final Map map = new HashMap<>(); for (final Header header: trailers) { map.put(TextUtils.toLowerCase(header.getName()), header.getValue()); } final String digest = TextUtils.toHexString(entityConsumer.getDigest()); Assertions.assertEquals("MD5", map.get("digest-algo")); Assertions.assertEquals(digest, map.get("digest")); } @Test public void testConnectionPing() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final int n = 10; final CountDownLatch latch = new CountDownLatch(n); final AtomicInteger count = new AtomicInteger(0); for (int i = 0; i < n; i++) { streamEndpoint.execute( new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); streamEndpoint.execute(new PingCommand(new BasicPingHandler(result -> { if (result) { count.incrementAndGet(); } latch.countDown(); })), Command.Priority.NORMAL); } Assertions.assertTrue(latch.await(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); Assertions.assertEquals(n, count.get()); } @Test public void testRequestWithInvalidConnectionHeader() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(); client.start(); final Future sessionFuture = client.requestSession(new HttpHost("localhost", serverEndpoint.getPort()), TIMEOUT, null); final IOSession session = sessionFuture.get(); try (final ClientSessionEndpoint streamEndpoint = new ClientSessionEndpoint(session)) { final HttpRequest request = new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/hello")); request.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); final HttpCoreContext coreContext = HttpCoreContext.create(); final Future> future = streamEndpoint.execute( new BasicRequestProducer(request, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), coreContext, null); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit())); assertThat(exception.getCause(), CoreMatchers.instanceOf(ProtocolException.class)); final EndpointDetails endpointDetails = coreContext.getEndpointDetails(); assertThat(endpointDetails.getRequestCount(), CoreMatchers.equalTo(0L)); } } @Test public void testHeaderTooLarge() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(H2Config.custom() .setMaxHeaderListSize(100) .build()); client.start(); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/hello")); request1.setHeader("big-f-header", "1234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890"); final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(431, response1.getCode()); Assertions.assertEquals("Maximum header list size exceeded", result1.getBody()); } @Test public void testHeaderTooLargePost() throws Exception { final H2TestServer server = resources.server(); final H2TestClient client = resources.client(); server.register("/hello", () -> new SingleLineResponseHandler("Hi there")); final InetSocketAddress serverEndpoint = server.start(H2Config.custom() .setMaxHeaderListSize(100) .build()); client.start( new DefaultHttpProcessor(H2RequestContent.INSTANCE, H2RequestTargetHost.INSTANCE, H2RequestConnControl.INSTANCE), H2Config.DEFAULT); final Future connectFuture = client.connect( "localhost", serverEndpoint.getPort(), TIMEOUT); final ClientSessionEndpoint streamEndpoint = connectFuture.get(); final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/hello")); request1.setHeader("big-f-header", "1234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890"); final byte[] b = new byte[2048]; for (int i = 0; i < b.length; i++) { b[i] = (byte) ('a' + i % 10); } final Future> future1 = streamEndpoint.execute( new BasicRequestProducer(request1, AsyncEntityProducers.create(b, ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(result1); final HttpResponse response1 = result1.getHead(); Assertions.assertNotNull(response1); Assertions.assertEquals(431, response1.getCode()); Assertions.assertEquals("Maximum header list size exceeded", result1.getBody()); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/HttpCoreTransportTest.java0100664 0000000 0000000 00000037065 14403631147 030735 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.net.InetSocketAddress; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.Future; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; public abstract class HttpCoreTransportTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); final URIScheme scheme; HttpCoreTransportTest(final URIScheme scheme) { this.scheme = scheme; } abstract HttpAsyncServer serverStart() throws IOException; abstract HttpAsyncRequester clientStart() throws IOException; @Test public void testSequentialRequests() throws Exception { final HttpAsyncServer server = serverStart(); final Future future = server.listen(new InetSocketAddress(0), scheme); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientStart(); final HttpHost target = new HttpHost(scheme.id, "localhost", address.getPort()); final Future> resultFuture1 = requester.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = message1.getBody(); assertThat(body1, CoreMatchers.equalTo("some stuff")); final Future> resultFuture2 = requester.execute( new BasicRequestProducer(Method.POST, target, "/other-stuff", new StringAsyncEntityProducer("some other stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message2, CoreMatchers.notNullValue()); final HttpResponse response2 = message2.getHead(); assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = message2.getBody(); assertThat(body2, CoreMatchers.equalTo("some other stuff")); final Future> resultFuture3 = requester.execute( new BasicRequestProducer(Method.POST, target, "/more-stuff", new StringAsyncEntityProducer("some more stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message3 = resultFuture3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message3, CoreMatchers.notNullValue()); final HttpResponse response3 = message3.getHead(); assertThat(response3.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body3 = message3.getBody(); assertThat(body3, CoreMatchers.equalTo("some more stuff")); } @Test public void testSequentialRequestsNonPersistentConnection() throws Exception { final HttpAsyncServer server = serverStart(); final Future future = server.listen(new InetSocketAddress(0), scheme); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientStart(); final HttpHost target = new HttpHost(scheme.id, "localhost", address.getPort()); final Future> resultFuture1 = requester.execute( new BasicRequestProducer(Method.POST, target, "/no-keep-alive/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = message1.getBody(); assertThat(body1, CoreMatchers.equalTo("some stuff")); final Future> resultFuture2 = requester.execute( new BasicRequestProducer(Method.POST, target, "/no-keep-alive/other-stuff", new StringAsyncEntityProducer("some other stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message2, CoreMatchers.notNullValue()); final HttpResponse response2 = message2.getHead(); assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = message2.getBody(); assertThat(body2, CoreMatchers.equalTo("some other stuff")); final Future> resultFuture3 = requester.execute( new BasicRequestProducer(Method.POST, target, "/no-keep-alive/more-stuff", new StringAsyncEntityProducer("some more stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message3 = resultFuture3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message3, CoreMatchers.notNullValue()); final HttpResponse response3 = message3.getHead(); assertThat(response3.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body3 = message3.getBody(); assertThat(body3, CoreMatchers.equalTo("some more stuff")); } @Test public void testSequentialRequestsSameEndpoint() throws Exception { final HttpAsyncServer server = serverStart(); final Future future = server.listen(new InetSocketAddress(0), scheme); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientStart(); final HttpHost target = new HttpHost(scheme.id, "localhost", address.getPort()); final Future endpointFuture = requester.connect(target, Timeout.ofSeconds(5)); final AsyncClientEndpoint endpoint = endpointFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); try { final Future> resultFuture1 = endpoint.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = message1.getBody(); assertThat(body1, CoreMatchers.equalTo("some stuff")); final Future> resultFuture2 = endpoint.execute( new BasicRequestProducer(Method.POST, target, "/other-stuff", new StringAsyncEntityProducer("some other stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message2, CoreMatchers.notNullValue()); final HttpResponse response2 = message2.getHead(); assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = message2.getBody(); assertThat(body2, CoreMatchers.equalTo("some other stuff")); final Future> resultFuture3 = endpoint.execute( new BasicRequestProducer(Method.POST, target, "/more-stuff", new StringAsyncEntityProducer("some more stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message message3 = resultFuture3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message3, CoreMatchers.notNullValue()); final HttpResponse response3 = message3.getHead(); assertThat(response3.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body3 = message3.getBody(); assertThat(body3, CoreMatchers.equalTo("some more stuff")); } finally { endpoint.releaseAndReuse(); } } @Test public void testPipelinedRequests() throws Exception { final HttpAsyncServer server = serverStart(); final Future future = server.listen(new InetSocketAddress(0), scheme); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientStart(); final HttpHost target = new HttpHost(scheme.id, "localhost", address.getPort()); final Future endpointFuture = requester.connect(target, Timeout.ofSeconds(5)); final AsyncClientEndpoint endpoint = endpointFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); try { final Queue>> queue = new LinkedList<>(); queue.add(endpoint.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); queue.add(endpoint.execute( new BasicRequestProducer(Method.POST, target, "/other-stuff", new StringAsyncEntityProducer("some other stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); queue.add(endpoint.execute( new BasicRequestProducer(Method.POST, target, "/more-stuff", new StringAsyncEntityProducer("some more stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null)); while (!queue.isEmpty()) { final Future> resultFuture = queue.remove(); final Message message = resultFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message, CoreMatchers.notNullValue()); final HttpResponse response = message.getHead(); assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body = message.getBody(); assertThat(body, CoreMatchers.containsString("stuff")); } } finally { endpoint.releaseAndReuse(); } } @Test public void testNonPersistentHeads() throws Exception { final HttpAsyncServer server = serverStart(); final Future future = server.listen(new InetSocketAddress(0), scheme); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientStart(); final HttpHost target = new HttpHost(scheme.id, "localhost", address.getPort()); final Queue>> queue = new LinkedList<>(); for (int i = 0; i < 20; i++) { final HttpRequest head = new BasicHttpRequest(Method.HEAD, target, "/no-keep-alive/stuff?p=" + i); queue.add(requester.execute( new BasicRequestProducer(head, null), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null)); } while (!queue.isEmpty()) { final Future> resultFuture = queue.remove(); final Message message = resultFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message, CoreMatchers.notNullValue()); final HttpResponse response = message.getHead(); assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); assertThat(message.getBody(), CoreMatchers.nullValue()); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/H2CoreTransportTest.java0100664 0000000 0000000 00000006203 14403631147 030255 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.io.IOException; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.protocol.UriPatternMatcher; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.testing.nio.extension.H2AsyncRequesterResource; import org.apache.hc.core5.testing.nio.extension.H2AsyncServerResource; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class H2CoreTransportTest extends HttpCoreTransportTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private final H2AsyncServerResource serverResource; @RegisterExtension private final H2AsyncRequesterResource clientResource; public H2CoreTransportTest(final URIScheme scheme) { super(scheme); this.serverResource = new H2AsyncServerResource(bootstrap -> bootstrap .setVersionPolicy(HttpVersionPolicy.NEGOTIATE) .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setLookupRegistry(new UriPatternMatcher<>()) .register("*", () -> new EchoHandler(2048)) ); this.clientResource = new H2AsyncRequesterResource(bootstrap -> bootstrap .setVersionPolicy(HttpVersionPolicy.NEGOTIATE) .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) ); } @Override HttpAsyncServer serverStart() throws IOException { return serverResource.start(); } @Override HttpAsyncRequester clientStart() { return clientResource.start(); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/HttpProtocolTests.java0100664 0000000 0000000 00000004066 14403631147 030107 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import org.apache.hc.core5.http.URIScheme; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; public class HttpProtocolTests { @Nested @DisplayName("HTTP/1.1 (plain)") public class Http1 extends Http1IntegrationTest { public Http1() { super(URIScheme.HTTP); } } @Nested @DisplayName("HTTP/1.1 (TLS)") public class Http1Tls extends Http1IntegrationTest { public Http1Tls() { super(URIScheme.HTTPS); } } @Nested @DisplayName("HTTP/2 (plain)") public class H2 extends H2IntegrationTest { public H2() { super(URIScheme.HTTP); } } @Nested @DisplayName("HTTP/2 (TLS)") public class H2Tls extends H2IntegrationTest { public H2Tls() { super(URIScheme.HTTPS); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/TestDefaultListeningIOReactor.java0100664 0000000 0000000 00000010625 14403631147 032272 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import java.net.InetSocketAddress; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.DefaultListeningIOReactor; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOReactorStatus; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Basic tests for {@link DefaultListeningIOReactor}. */ public class TestDefaultListeningIOReactor { private DefaultListeningIOReactor ioReactor; @BeforeEach public void setup() throws Exception { final IOReactorConfig reactorConfig = IOReactorConfig.custom() .setIoThreadCount(1) .build(); this.ioReactor = new DefaultListeningIOReactor(new NoopIOEventHandlerFactory(), reactorConfig, null); } @AfterEach public void cleanup() throws Exception { if (this.ioReactor != null) { this.ioReactor.close(CloseMode.IMMEDIATE); } } @Test public void testEndpointUpAndDown() throws Exception { ioReactor.start(); Set endpoints = ioReactor.getEndpoints(); Assertions.assertNotNull(endpoints); Assertions.assertEquals(0, endpoints.size()); final Future future1 = ioReactor.listen(new InetSocketAddress(0)); final ListenerEndpoint endpoint1 = future1.get(); final Future future2 = ioReactor.listen(new InetSocketAddress(0)); final ListenerEndpoint endpoint2 = future2.get(); final int port = ((InetSocketAddress) endpoint2.getAddress()).getPort(); endpoints = ioReactor.getEndpoints(); Assertions.assertNotNull(endpoints); Assertions.assertEquals(2, endpoints.size()); endpoint1.close(); endpoints = ioReactor.getEndpoints(); Assertions.assertNotNull(endpoints); Assertions.assertEquals(1, endpoints.size()); final ListenerEndpoint endpoint = endpoints.iterator().next(); Assertions.assertEquals(port, ((InetSocketAddress) endpoint.getAddress()).getPort()); ioReactor.close(CloseMode.GRACEFUL); ioReactor.awaitShutdown(TimeValue.ofSeconds(5)); Assertions.assertEquals(IOReactorStatus.SHUT_DOWN, ioReactor.getStatus()); } @Test public void testEndpointAlreadyBound() throws Exception { ioReactor.start(); final Future future1 = ioReactor.listen(new InetSocketAddress(0)); final ListenerEndpoint endpoint1 = future1.get(); final int port = ((InetSocketAddress) endpoint1.getAddress()).getPort(); final Future future2 = ioReactor.listen(new InetSocketAddress(port)); Assertions.assertThrows(ExecutionException.class, () -> future2.get()); ioReactor.close(CloseMode.GRACEFUL); ioReactor.awaitShutdown(TimeValue.ofSeconds(5)); Assertions.assertEquals(IOReactorStatus.SHUT_DOWN, ioReactor.getStatus()); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/TLSUpgradeTest.java0100664 0000000 0000000 00000015542 14403631147 027236 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import static org.hamcrest.MatcherAssert.assertThat; import java.net.InetSocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureContribution; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.ssl.TlsUpgradeCapable; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.UriPatternMatcher; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.testing.nio.extension.HttpAsyncRequesterResource; import org.apache.hc.core5.testing.nio.extension.HttpAsyncServerResource; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public class TLSUpgradeTest { private static final Timeout TIMEOUT = Timeout.ofSeconds(30); @RegisterExtension private final HttpAsyncServerResource serverResource; @RegisterExtension private final HttpAsyncRequesterResource clientResource; public TLSUpgradeTest() { this.serverResource = new HttpAsyncServerResource(bootstrap -> bootstrap .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setLookupRegistry(new UriPatternMatcher<>()) .register("*", () -> new EchoHandler(2048)) ); this.clientResource = new HttpAsyncRequesterResource(bootstrap -> bootstrap .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) ); } @Test public void testTLSUpgrade() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), URIScheme.HTTPS); final ListenerEndpoint listener = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientResource.start(); final HttpHost target = new HttpHost(URIScheme.HTTPS.id, "localhost", address.getPort()); final Future> resultFuture1 = requester.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = message1.getBody(); assertThat(body1, CoreMatchers.equalTo("some stuff")); // Connect using plain HTTP scheme final Future endpointFuture = requester.connect( new HttpHost(URIScheme.HTTP.id, "localhost", address.getPort()), TIMEOUT); final AsyncClientEndpoint clientEndpoint = endpointFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertInstanceOf(TlsUpgradeCapable.class, clientEndpoint); // Upgrade to TLS final BasicFuture tlsFuture = new BasicFuture<>(null); ((TlsUpgradeCapable) clientEndpoint).tlsUpgrade(target, new FutureContribution(tlsFuture) { @Override public void completed(final ProtocolIOSession protocolIOSession) { tlsFuture.completed(protocolIOSession.getTlsDetails()); } }); final TlsDetails tlsDetails = tlsFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); Assertions.assertNotNull(tlsDetails); // Execute request over HTTPS final Future> resultFuture2 = clientEndpoint.execute( new BasicRequestProducer(Method.POST, target, "/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null); final Message message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message2, CoreMatchers.notNullValue()); final HttpResponse response2 = message2.getHead(); assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = message2.getBody(); assertThat(body2, CoreMatchers.equalTo("some stuff")); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1CoreTransportTest.java0100664 0000000 0000000 00000021136 14403631147 031006 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.nio; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.impl.bootstrap.StandardFilter; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncFilterChain; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.UriPatternMatcher; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.testing.nio.extension.HttpAsyncRequesterResource; import org.apache.hc.core5.testing.nio.extension.HttpAsyncServerResource; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class Http1CoreTransportTest extends HttpCoreTransportTest { private static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private final HttpAsyncServerResource serverResource; @RegisterExtension private final HttpAsyncRequesterResource clientResource; public Http1CoreTransportTest(final URIScheme scheme) { super(scheme); this.serverResource = new HttpAsyncServerResource(bootstrap -> bootstrap .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) .setLookupRegistry(new UriPatternMatcher<>()) .register("*", () -> new EchoHandler(2048)) .addFilterBefore(StandardFilter.MAIN_HANDLER.name(), "no-keepalive", (request, entityDetails, context, responseTrigger, chain) -> chain.proceed(request, entityDetails, context, new AsyncFilterChain.ResponseTrigger() { @Override public void sendInformation( final HttpResponse response) throws HttpException, IOException { responseTrigger.sendInformation(response); } @Override public void submitResponse( final HttpResponse response, final AsyncEntityProducer entityProducer) throws HttpException, IOException { if (request.getPath().startsWith("/no-keep-alive")) { response.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); } responseTrigger.submitResponse(response, entityProducer); } @Override public void pushPromise( final HttpRequest promise, final AsyncPushProducer responseProducer) throws HttpException, IOException { responseTrigger.pushPromise(promise, responseProducer); } })) ); this.clientResource = new HttpAsyncRequesterResource(bootstrap -> bootstrap .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(TIMEOUT) .build()) ); } @Override HttpAsyncServer serverStart() throws IOException { return serverResource.start(); } @Override HttpAsyncRequester clientStart() { return clientResource.start(); } @Test public void testSequentialRequestsNonPersistentConnection() throws Exception { final HttpAsyncServer server = serverResource.start(); final Future future = server.listen(new InetSocketAddress(0), scheme); final ListenerEndpoint listener = future.get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); final HttpAsyncRequester requester = clientResource.start(); final HttpHost target = new HttpHost(scheme.id, "localhost", address.getPort()); final Future> resultFuture1 = requester.execute( new BasicRequestProducer(Method.POST, target, "/no-keep-alive/stuff", new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message1, CoreMatchers.notNullValue()); final HttpResponse response1 = message1.getHead(); assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body1 = message1.getBody(); assertThat(body1, CoreMatchers.equalTo("some stuff")); final Future> resultFuture2 = requester.execute( new BasicRequestProducer(Method.POST, target, "/no-keep-alive/other-stuff", new StringAsyncEntityProducer("some other stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message2, CoreMatchers.notNullValue()); final HttpResponse response2 = message2.getHead(); assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body2 = message2.getBody(); assertThat(body2, CoreMatchers.equalTo("some other stuff")); final Future> resultFuture3 = requester.execute( new BasicRequestProducer(Method.POST, target, "/no-keep-alive/more-stuff", new StringAsyncEntityProducer("some more stuff", ContentType.TEXT_PLAIN)), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null); final Message message3 = resultFuture3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(message3, CoreMatchers.notNullValue()); final HttpResponse response3 = message3.getHead(); assertThat(response3.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); final String body3 = message3.getBody(); assertThat(body3, CoreMatchers.equalTo("some more stuff")); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/reactive/0040775 0000000 0000000 00000000000 14403631147 024572 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/reactive/IntegrationTests.java0100664 0000000 0000000 00000003467 14403631147 030752 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.reactive; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; public class IntegrationTests { @Nested @DisplayName("Reactive client (HTTP/1.1)") public class CoreFunctions extends ReactiveClientTest { public CoreFunctions() { super(HttpVersionPolicy.FORCE_HTTP_1); } } @Nested @DisplayName("Reactive client (HTTP/2)") public class CoreFunctionsTls extends ReactiveClientTest { public CoreFunctionsTls() { super(HttpVersionPolicy.FORCE_HTTP_2); } } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/reactive/ReactiveClientTest.java0100664 0000000 0000000 00000034347 14403631147 031206 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.reactive; import static java.lang.String.format; import static org.hamcrest.MatcherAssert.assertThat; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketTimeoutException; import java.net.URI; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; import java.util.List; import java.util.Random; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Observable; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStreamResetException; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.reactive.ReactiveEntityProducer; import org.apache.hc.core5.reactive.ReactiveResponseConsumer; import org.apache.hc.core5.reactive.ReactiveServerExchangeHandler; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.testing.nio.extension.H2AsyncRequesterResource; import org.apache.hc.core5.testing.nio.extension.H2AsyncServerResource; import org.apache.hc.core5.testing.reactive.Reactive3TestUtils.StreamDescription; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.reactivestreams.Publisher; public abstract class ReactiveClientTest { private static final Timeout SOCKET_TIMEOUT = Timeout.ofSeconds(30); private static final Timeout RESULT_TIMEOUT = Timeout.ofSeconds(60); private static final Random RANDOM = new Random(); private final HttpVersionPolicy versionPolicy; @RegisterExtension private final H2AsyncServerResource serverResource; @RegisterExtension private final H2AsyncRequesterResource clientResource; public ReactiveClientTest(final HttpVersionPolicy httpVersionPolicy) { this.versionPolicy = httpVersionPolicy; this.serverResource = new H2AsyncServerResource(bootstrap -> bootstrap .setVersionPolicy(versionPolicy) .setIOReactorConfig( IOReactorConfig.custom() .setSoTimeout(SOCKET_TIMEOUT) .build()) .register("*", () -> new ReactiveServerExchangeHandler(new ReactiveEchoProcessor())) ); this.clientResource = new H2AsyncRequesterResource(bootstrap -> bootstrap .setVersionPolicy(versionPolicy) .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(SOCKET_TIMEOUT) .build()) ); } @Test public void testSimpleRequest() throws Exception { final InetSocketAddress address = startServer(); final HttpAsyncRequester requester = clientResource.start(); final byte[] input = new byte[1024]; RANDOM.nextBytes(input); final Publisher publisher = Flowable.just(ByteBuffer.wrap(input)); final ReactiveEntityProducer producer = new ReactiveEntityProducer(publisher, input.length, null, null); final BasicRequestProducer request = getRequestProducer(address, producer); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); requester.execute(request, consumer, SOCKET_TIMEOUT, null); final Message> response = consumer.getResponseFuture() .get(RESULT_TIMEOUT.getDuration(), RESULT_TIMEOUT.getTimeUnit()); final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); final WritableByteChannel writableByteChannel = Channels.newChannel(byteArrayOutputStream); for (final ByteBuffer byteBuffer : Observable.fromPublisher(response.getBody()).toList().blockingGet()) { writableByteChannel.write(byteBuffer); } writableByteChannel.close(); final byte[] output = byteArrayOutputStream.toByteArray(); Assertions.assertArrayEquals(input, output); } private BasicRequestProducer getRequestProducer(final InetSocketAddress address, final ReactiveEntityProducer producer) { return new BasicRequestProducer(Method.POST, URI.create("http://localhost:" + address.getPort()), producer); } @Test public void testLongRunningRequest() throws Exception { final InetSocketAddress address = startServer(); final HttpAsyncRequester requester = clientResource.start(); final long expectedLength = 6_554_200L; final AtomicReference expectedHash = new AtomicReference<>(); final Flowable stream = Reactive3TestUtils.produceStream(expectedLength, expectedHash); final ReactiveEntityProducer producer = new ReactiveEntityProducer(stream, -1, null, null); final BasicRequestProducer request = getRequestProducer(address, producer); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); requester.execute(request, consumer, SOCKET_TIMEOUT, null); final Message> response = consumer.getResponseFuture() .get(RESULT_TIMEOUT.getDuration(), RESULT_TIMEOUT.getTimeUnit()); final StreamDescription desc = Reactive3TestUtils.consumeStream(response.getBody()).blockingGet(); Assertions.assertEquals(expectedLength, desc.length); Assertions.assertEquals(expectedHash.get(), TextUtils.toHexString(desc.md.digest())); } @Test public void testManySmallBuffers() throws Exception { // This test is not flaky. If it starts randomly failing, then there is a problem with how // ReactiveDataConsumer signals capacity with its capacity channel. The situations in which // this kind of bug manifests depend on the ordering of several events on different threads // so it's unlikely to consistently occur. final InetSocketAddress address = startServer(); final HttpAsyncRequester requester = clientResource.start(); for (int i = 0; i < 10; i++) { final long expectedLength = 1_024_000; final int maximumBlockSize = 1024; final AtomicReference expectedHash = new AtomicReference<>(); final Publisher stream = Reactive3TestUtils.produceStream(expectedLength, maximumBlockSize, expectedHash); final ReactiveEntityProducer producer = new ReactiveEntityProducer(stream, -1, null, null); final BasicRequestProducer request = getRequestProducer(address, producer); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); requester.execute(request, consumer, SOCKET_TIMEOUT, null); final Message> response = consumer.getResponseFuture() .get(RESULT_TIMEOUT.getDuration(), RESULT_TIMEOUT.getTimeUnit()); final StreamDescription desc = Reactive3TestUtils.consumeStream(response.getBody()).blockingGet(); Assertions.assertEquals(expectedLength, desc.length); Assertions.assertEquals(expectedHash.get(), TextUtils.toHexString(desc.md.digest())); } } @Test public void testRequestError() throws Exception { final InetSocketAddress address = startServer(); final HttpAsyncRequester requester = clientResource.start(); final RuntimeException exceptionThrown = new RuntimeException("Test"); final Publisher publisher = Flowable.error(exceptionThrown); final ReactiveEntityProducer producer = new ReactiveEntityProducer(publisher, 100, null, null); final BasicRequestProducer request = getRequestProducer(address, producer); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); final Future future = requester.execute(request, consumer, SOCKET_TIMEOUT, null); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> future.get(RESULT_TIMEOUT.getDuration(), RESULT_TIMEOUT.getTimeUnit())); Assertions.assertTrue(exception.getCause() instanceof HttpStreamResetException); Assertions.assertSame(exceptionThrown, exception.getCause().getCause()); } @Test public void testRequestTimeout() throws Exception { final InetSocketAddress address = startServer(); final HttpAsyncRequester requester = clientResource.start(); final AtomicBoolean requestPublisherWasCancelled = new AtomicBoolean(false); final Publisher publisher = Flowable.never() .doOnCancel(() -> requestPublisherWasCancelled.set(true)); final ReactiveEntityProducer producer = new ReactiveEntityProducer(publisher, -1, null, null); final BasicRequestProducer request = getRequestProducer(address, producer); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); final Future future = requester.execute(request, consumer, Timeout.ofSeconds(1), null); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> future.get(RESULT_TIMEOUT.getDuration(), RESULT_TIMEOUT.getTimeUnit())); Assertions.assertTrue(requestPublisherWasCancelled.get()); final Throwable cause = exception.getCause(); if (versionPolicy == HttpVersionPolicy.FORCE_HTTP_1) { Assertions.assertTrue(cause instanceof SocketTimeoutException, "Expected SocketTimeoutException, but got " + cause.getClass().getName()); } else if (versionPolicy == HttpVersionPolicy.FORCE_HTTP_2) { Assertions.assertTrue(cause instanceof HttpStreamResetException, format("Expected RST_STREAM, but %s was thrown", cause.getClass().getName())); } else { Assertions.fail("Unknown HttpVersionPolicy: " + versionPolicy); } } @Test public void testResponseCancellation() throws Exception { final InetSocketAddress address = startServer(); final HttpAsyncRequester requester = clientResource.start(); final AtomicBoolean requestPublisherWasCancelled = new AtomicBoolean(false); final AtomicReference requestStreamError = new AtomicReference<>(); final Publisher stream = Reactive3TestUtils.produceStream(Long.MAX_VALUE, 1024, null) .doOnCancel(() -> requestPublisherWasCancelled.set(true)) .doOnError(requestStreamError::set); final ReactiveEntityProducer producer = new ReactiveEntityProducer(stream, -1, null, null); final BasicRequestProducer request = getRequestProducer(address, producer); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); final Future future = requester.execute(request, consumer, SOCKET_TIMEOUT, null); final Message> response = consumer.getResponseFuture() .get(RESULT_TIMEOUT.getDuration(), RESULT_TIMEOUT.getTimeUnit()); final AtomicBoolean responsePublisherWasCancelled = new AtomicBoolean(false); final List outputBuffers = Flowable.fromPublisher(response.getBody()) .doOnCancel(() -> responsePublisherWasCancelled.set(true)) .take(3) .toList() .blockingGet(); Assertions.assertEquals(3, outputBuffers.size()); Assertions.assertTrue(responsePublisherWasCancelled.get(), "The response subscription should have been cancelled"); final Exception exception = Assertions.assertThrows(Exception.class, () -> future.get(RESULT_TIMEOUT.getDuration(), RESULT_TIMEOUT.getTimeUnit())); assertThat(exception, CoreMatchers.anyOf( CoreMatchers.instanceOf(CancellationException.class), CoreMatchers.instanceOf(ExecutionException.class))); Assertions.assertTrue(exception.getCause() instanceof HttpStreamResetException); Assertions.assertTrue(requestPublisherWasCancelled.get()); Assertions.assertNull(requestStreamError.get()); } private InetSocketAddress startServer() throws IOException, InterruptedException, ExecutionException { final HttpAsyncServer server = serverResource.start(); final ListenerEndpoint listener = server.listen(new InetSocketAddress(0), URIScheme.HTTP).get(); final InetSocketAddress address = (InetSocketAddress) listener.getAddress(); return address; } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/0040775 0000000 0000000 00000000000 14403631147 024765 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestClientTestingAdapter.java0100664 0000000 0000000 00000003740 14403631147 032546 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestClientTestingAdapter { @Test public void getHttpClientPOJOAdapter() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final ClientPOJOAdapter pojoAdapter = adapter.getClientPOJOAdapter(); Assertions.assertNotNull(pojoAdapter, "pojoAdapter should not be null"); } @Test public void isRequestSupported() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final Map request = null; Assertions.assertTrue(adapter.isRequestSupported(request), "isRequestSupported should return true"); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestFrameworkTest.java0100664 0000000 0000000 00000012042 14403631147 031261 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.BODY; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.CONTENT_TYPE; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.HEADERS; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.METHOD; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.PROTOCOL_VERSION; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.QUERY; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.REQUEST; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.RESPONSE; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.STATUS; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestFrameworkTest { @Test public void defaults() throws Exception { final FrameworkTest test = new FrameworkTest(); final Map request = test.initRequest(); Assertions.assertNotNull(request, "request should not be null"); Assertions.assertEquals(request.get(METHOD), "GET", "Default method should be GET"); Assertions.assertEquals(TestingFramework.DEFAULT_REQUEST_BODY, request.get(BODY), "Default request body expected."); Assertions.assertEquals(TestingFramework.DEFAULT_REQUEST_CONTENT_TYPE, request.get(CONTENT_TYPE), "Default request content type expected."); Assertions.assertEquals(TestingFramework.DEFAULT_REQUEST_QUERY, request.get(QUERY), "Default request query parameters expected."); Assertions.assertEquals(TestingFramework.DEFAULT_REQUEST_HEADERS, request.get(HEADERS), "Default request headers expected."); Assertions.assertEquals(TestingFramework.DEFAULT_REQUEST_PROTOCOL_VERSION, request.get(PROTOCOL_VERSION), "Default protocol version expected."); final Map responseExpectations = test.initResponseExpectations(); Assertions.assertNotNull(responseExpectations, "responseExpectations should not be null"); Assertions.assertEquals(TestingFramework.DEFAULT_RESPONSE_STATUS, responseExpectations.get(STATUS), "Default status expected."); Assertions.assertEquals(TestingFramework.DEFAULT_RESPONSE_BODY, responseExpectations.get(BODY), "Default body expected."); Assertions.assertEquals(TestingFramework.DEFAULT_RESPONSE_CONTENT_TYPE, responseExpectations.get(CONTENT_TYPE), "Default response content type expected."); Assertions.assertEquals(TestingFramework.DEFAULT_RESPONSE_HEADERS, responseExpectations.get(HEADERS), "Default headers expected."); } @Test public void changeStatus() throws Exception { final Map testMap = new HashMap<>(); final Map response = new HashMap<>(); testMap.put(RESPONSE, response); response.put(STATUS, 201); final FrameworkTest test = new FrameworkTest(testMap); final Map responseExpectations = test.initResponseExpectations(); Assertions.assertEquals(201, responseExpectations.get(STATUS), "Status unexpected."); } @Test public void changeMethod() throws Exception { final Map testMap = new HashMap<>(); final Map request = new HashMap<>(); testMap.put(REQUEST, request); request.put(METHOD, "POST"); final FrameworkTest test = new FrameworkTest(testMap); final Map requestExpectations = test.initRequest(); Assertions.assertEquals("POST", requestExpectations.get(METHOD), "Method unexpected."); } } ././@LongLink0100644 0000000 0000000 00000000156 14403631147 011640 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestTestingFrameworkRequestHandler.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestTestingFrameworkRequestHan0100664 0000000 0000000 00000005606 14403631147 033047 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestTestingFrameworkRequestHandler { @Test public void assertNothingThrown() throws Exception { final TestingFrameworkRequestHandler handler = new TestingFrameworkRequestHandler() { @Override public void handle(final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { } }; handler.assertNothingThrown(); } @Test public void assertNothingThrownThrows() throws Exception { final String errorMessage = "thrown intentionally"; final TestingFrameworkRequestHandler handler = new TestingFrameworkRequestHandler() { @Override public void handle(final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { thrown = new TestingFrameworkException(errorMessage); } }; handler.handle(null, null, null); final TestingFrameworkException exception = Assertions.assertThrows(TestingFrameworkException.class, () -> handler.assertNothingThrown()); Assertions.assertEquals(errorMessage, exception.getMessage(), "Unexpected message"); // a second call should not throw handler.assertNothingThrown(); } } ././@LongLink0100644 0000000 0000000 00000000157 14403631147 011641 Lustar 0000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestClassicTestClientTestingAdapter.javahttpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestClassicTestClientTestingAd0100664 0000000 0000000 00000025565 14403631147 032745 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.BODY; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.HEADERS; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.METHOD; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.PATH; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.STATUS; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.testing.classic.EchoHandler; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestClassicTestClientTestingAdapter { private static final String ECHO_PATH = "echo/something"; private static final String CUSTOM_PATH = "custom/something"; private ClassicTestServer server; @BeforeEach public void initServer() throws Exception { this.server = new ClassicTestServer(SocketConfig.custom() .setSoTimeout(5, TimeUnit.SECONDS).build()); } @AfterEach public void shutDownServer() throws Exception { if (this.server != null) { this.server.shutdown(CloseMode.IMMEDIATE); } } @Test public void nullDefaultURI() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final String defaultURI = null; final Map request = new HashMap<>(); final TestingFrameworkRequestHandler requestHandler = Mockito.mock(TestingFrameworkRequestHandler.class); final Map responseExpectations = new HashMap<>(); Assertions.assertThrows(TestingFrameworkException.class, () -> adapter.execute(defaultURI, request, requestHandler, responseExpectations)); } @Test public void nullRequest() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final String defaultURI = ""; final Map request = null; final TestingFrameworkRequestHandler requestHandler = Mockito.mock(TestingFrameworkRequestHandler.class); final Map responseExpectations = new HashMap<>(); Assertions.assertThrows(TestingFrameworkException.class, () -> adapter.execute(defaultURI, request, requestHandler, responseExpectations)); } @Test public void nullRequestHandler() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final String defaultURI = ""; final Map request = new HashMap<>(); final TestingFrameworkRequestHandler requestHandler = null; final Map responseExpectations = new HashMap<>(); Assertions.assertThrows(TestingFrameworkException.class, () -> adapter.execute(defaultURI, request, requestHandler, responseExpectations)); } @Test public void nullResponseExpectations() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final String defaultURI = ""; final Map request = new HashMap<>(); final TestingFrameworkRequestHandler requestHandler = Mockito.mock(TestingFrameworkRequestHandler.class); final Map responseExpectations = null; Assertions.assertThrows(TestingFrameworkException.class, () -> adapter.execute(defaultURI, request, requestHandler, responseExpectations)); } @Test public void noPath() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final String defaultURI = ""; final Map request = new HashMap<>(); final TestingFrameworkRequestHandler requestHandler = Mockito.mock(TestingFrameworkRequestHandler.class); final Map responseExpectations = new HashMap<>(); Assertions.assertThrows(TestingFrameworkException.class, () -> adapter.execute(defaultURI, request, requestHandler, responseExpectations)); } @Test public void noMethod() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final String defaultURI = ""; final Map request = new HashMap<>(); request.put(PATH, ECHO_PATH); final TestingFrameworkRequestHandler requestHandler = Mockito.mock(TestingFrameworkRequestHandler.class); final Map responseExpectations = new HashMap<>(); Assertions.assertThrows(TestingFrameworkException.class, () -> adapter.execute(defaultURI, request, requestHandler, responseExpectations)); } @Test public void invalidMethod() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final String defaultURI = ""; final Map request = new HashMap<>(); request.put(PATH, ECHO_PATH); request.put(METHOD, "JUNK"); final TestingFrameworkRequestHandler requestHandler = Mockito.mock(TestingFrameworkRequestHandler.class); final Map responseExpectations = new HashMap<>(); Assertions.assertThrows(TestingFrameworkException.class, () -> adapter.execute(defaultURI, request, requestHandler, responseExpectations)); } @Test public void withLiveServerEcho() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); // Initialize the server-side request handler server.registerHandler("/echo/*", new EchoHandler()); this.server.start(); final HttpHost target = new HttpHost("localhost", this.server.getPort()); final String defaultURI = target.toString(); final Map request = new HashMap<>(); request.put(PATH, ECHO_PATH); request.put(METHOD, Method.POST.name()); final String body = "mybody"; request.put(BODY, body); final Map responseExpectations = new HashMap<>(); final TestingFrameworkRequestHandler requestHandler = Mockito.mock(TestingFrameworkRequestHandler.class); final Map response = adapter.execute(defaultURI, request, requestHandler, responseExpectations); Assertions.assertNotNull(response, "response should not be null"); Assertions.assertEquals(200, response.get(STATUS), "status unexpected"); @SuppressWarnings("unchecked") final Map headers = (Map) response.get(HEADERS); Assertions.assertNotNull(headers, "headers should be in the response"); Assertions.assertFalse(headers.isEmpty()); final String returnedBody = (String) response.get(BODY); Assertions.assertNotNull(returnedBody, "body should be in the response"); Assertions.assertEquals(body, returnedBody, "Body should be echoed"); } @Test public void withLiveServerCustomRequestHandler() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final TestingFrameworkRequestHandler requestHandler = new TestingFrameworkRequestHandler() { @Override public void handle(final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { try { Assertions.assertEquals("method not expected", "junk", request.getMethod()); } catch (final Throwable t) { thrown = t; } } }; server.registerHandler("/custom/*", requestHandler); this.server.start(); final HttpHost target = new HttpHost("localhost", this.server.getPort()); final String defaultURI = target.toString(); final Map responseExpectations = new HashMap<>(); final Map request = new HashMap<>(); request.put(PATH, CUSTOM_PATH); for (final String method : TestingFramework.ALL_METHODS) { request.put(METHOD, method); adapter.execute(defaultURI, request, requestHandler, responseExpectations); } } @Test public void modifyRequest() { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final Map request = new HashMap<>(); final Map returnedRequest = adapter.modifyRequest(request); Assertions.assertSame(request, returnedRequest, "Same request was not returned as expected."); } @Test public void modifyResponseExpectations() { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final Map responseExpectations = new HashMap<>(); final Map returnedResponseExpectations = adapter.modifyResponseExpectations(null, responseExpectations); Assertions.assertSame(responseExpectations, returnedResponseExpectations, "Same response expectations were not returned as expected."); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestTestingFramework.java0100664 0000000 0000000 00000130252 14403631147 031763 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.BODY; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.CONTENT_TYPE; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.HEADERS; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.METHOD; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.NAME; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.PATH; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.PROTOCOL_VERSION; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.QUERY; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.REQUEST; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.RESPONSE; import static org.apache.hc.core5.testing.framework.ClientPOJOAdapter.STATUS; import static org.hamcrest.MatcherAssert.assertThat; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; public class TestTestingFramework { @Test public void ensureDefaultMapsUnmodifiable() throws Exception { assertUnmodifiable(TestingFramework.DEFAULT_REQUEST_QUERY); assertUnmodifiable(TestingFramework.DEFAULT_RESPONSE_HEADERS); } private void assertUnmodifiable(final Map map) { final String aKey = (String) map.keySet().toArray()[0]; Assertions.assertThrows(UnsupportedOperationException.class, () -> map.remove(aKey)); } private TestingFramework newWebServerTestingFramework(final ClientTestingAdapter adapter) throws TestingFrameworkException { final TestingFramework framework = new TestingFramework(adapter); // get rid of the default tests. framework.deleteTests(); return framework; } private TestingFramework newWebServerTestingFramework() throws TestingFrameworkException { return newWebServerTestingFramework(null); // null adapter } @Test public void runTestsWithoutSettingAdapterThrows() throws Exception { final TestingFramework framework = newWebServerTestingFramework(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void nullAdapterThrows() throws Exception { final ClientTestingAdapter adapter = null; final TestingFramework framework = newWebServerTestingFramework(adapter); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void nullSetAdapterThrows() throws Exception { final ClientTestingAdapter adapter = null; final TestingFramework framework = newWebServerTestingFramework(adapter); framework.setAdapter(adapter); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void goodAdapterWithConstructor() throws Exception { final ClientTestingAdapter adapter = Mockito.mock(ClientTestingAdapter.class); // Have isRequestSupported() return false so no test will run. Mockito.when(adapter.isRequestSupported(ArgumentMatchers.anyMap())) .thenReturn(false); final TestingFramework framework = newWebServerTestingFramework(adapter); framework.runTests(); // since there are no tests, callMethod should not be called. verifyCallMethodNeverCalled(adapter); } private void verifyCallMethodNeverCalled(final ClientTestingAdapter adapter) throws Exception { Mockito.verify(adapter, Mockito.never()).execute(ArgumentMatchers.anyString(), ArgumentMatchers.anyMap(), ArgumentMatchers.any(TestingFrameworkRequestHandler.class), ArgumentMatchers.anyMap()); } private TestingFramework newFrameworkAndSetAdapter(final ClientTestingAdapter adapter) throws TestingFrameworkException { final TestingFramework framework = new TestingFramework(); framework.setAdapter(adapter); // get rid of the default tests. framework.deleteTests(); return framework; } @Test public void goodAdapterWithSetter() throws Exception { final ClientTestingAdapter adapter = Mockito.mock(ClientTestingAdapter.class); final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.runTests(); // since there are no tests, callMethod should not be called. verifyCallMethodNeverCalled(adapter); } @Test public void addTest() throws Exception { final TestingFrameworkRequestHandler mockRequestHandler = Mockito.mock(TestingFrameworkRequestHandler.class); final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { assertThat(defaultURI, matchesDefaultURI()); Assertions.assertNotNull(request, "request should not be null"); // The request should be equal to the default request. final Map defaultRequest = new FrameworkTest().initRequest(); Assertions.assertEquals(defaultRequest, request, "The request does not match the default"); Assertions.assertSame(mockRequestHandler, requestHandler, "The request handler should have been passed to the adapter"); // The responseExpectations should be equal to the default. final Map defaultResponseExpectations = new FrameworkTest().initResponseExpectations(); Assertions.assertEquals(defaultResponseExpectations, responseExpectations, "The responseExpectations do not match the defaults"); final Map response = new HashMap<>(); response.put(STATUS, responseExpectations.get(STATUS)); response.put(BODY, responseExpectations.get(BODY)); response.put(CONTENT_TYPE, responseExpectations.get(CONTENT_TYPE)); response.put(HEADERS, responseExpectations.get(HEADERS)); return response; } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.setRequestHandler(mockRequestHandler); framework.addTest(); framework.runTests(); // assertNothingThrown() should have been called. Mockito.verify(mockRequestHandler).assertNothingThrown(); } private Matcher matchesDefaultURI() { final Matcher matcher = new BaseMatcher() { private final String regex = "http://localhost:\\d+/"; @Override public boolean matches(final Object o) { return ((String) o).matches(regex); } @Override public void describeTo(final Description description) { description.appendText("matches regex=" + regex); } }; return matcher; } @Test public void statusCheck() throws Exception { final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) { Assertions.assertEquals(200, responseExpectations.get(STATUS)); // return a different status than expected. final Map response = new HashMap<>(); response.put(STATUS, 201); return response; } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } private Map alreadyCheckedResponse() { // return an indication that the response has already been checked. final Map response = new HashMap<>(); response.put(STATUS, TestingFramework.ALREADY_CHECKED); response.put(BODY, TestingFramework.ALREADY_CHECKED); response.put(CONTENT_TYPE, TestingFramework.ALREADY_CHECKED); response.put(HEADERS, TestingFramework.ALREADY_CHECKED); return response; } @Test public void responseAlreadyChecked() throws Exception { final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) { return alreadyCheckedResponse(); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); framework.runTests(); } @Test public void bodyCheck() throws Exception { final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) { Assertions.assertEquals(TestingFramework.DEFAULT_RESPONSE_BODY, responseExpectations.get(BODY)); final Map response = new HashMap<>(); response.put(STATUS, TestingFramework.ALREADY_CHECKED); // return a different body than expected. response.put(BODY, TestingFramework.DEFAULT_RESPONSE_BODY + "junk"); return response; } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void responseContentTypeCheck() throws Exception { final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) { Assertions.assertEquals(TestingFramework.DEFAULT_RESPONSE_CONTENT_TYPE, responseExpectations.get(CONTENT_TYPE)); final Map response = new HashMap<>(); response.put(STATUS, TestingFramework.ALREADY_CHECKED); response.put(HEADERS, TestingFramework.ALREADY_CHECKED); // return the expected body response.put(BODY, TestingFramework.DEFAULT_RESPONSE_BODY); // return a different content type than expected. response.put(CONTENT_TYPE, ContentType.DEFAULT_TEXT); return response; } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void deepcopy() throws Exception { // save a copy of the headers to make sure they haven't changed at the end of this test. @SuppressWarnings("unchecked") final Map headersCopy = (Map) TestingFramework.deepcopy(TestingFramework.DEFAULT_RESPONSE_HEADERS); Assertions.assertEquals(TestingFramework.DEFAULT_RESPONSE_HEADERS, headersCopy); final Map deepMap = new HashMap<>(); deepMap.put(HEADERS, TestingFramework.DEFAULT_RESPONSE_HEADERS); @SuppressWarnings("unchecked") final Map deepMapCopy = (Map) TestingFramework.deepcopy(deepMap); Assertions.assertEquals(deepMap, deepMapCopy); @SuppressWarnings("unchecked") final Map headersMap = (Map) deepMapCopy.get(HEADERS); Assertions.assertEquals(headersCopy, headersMap); // now make sure the default headers have not changed for some unexpected reason. Assertions.assertEquals(TestingFramework.DEFAULT_RESPONSE_HEADERS, headersCopy); } @Test public void removedHeaderCheck() throws Exception { final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) { Assertions.assertEquals(TestingFramework.DEFAULT_RESPONSE_HEADERS, responseExpectations.get(HEADERS)); @SuppressWarnings("unchecked") final Map headersCopy = (Map) deepcopy(responseExpectations.get(HEADERS)); // remove a header to force an error final String headerName = (String) headersCopy.keySet().toArray()[0]; headersCopy.remove(headerName); final Map response = new HashMap<>(); response.put(STATUS, TestingFramework.ALREADY_CHECKED); response.put(BODY, TestingFramework.ALREADY_CHECKED); // return different headers than expected. response.put(HEADERS, headersCopy); return response; } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void changedHeaderCheck() throws Exception { final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) { Assertions.assertEquals(TestingFramework.DEFAULT_RESPONSE_HEADERS, responseExpectations.get(HEADERS)); @SuppressWarnings("unchecked") final Map headersCopy = (Map) deepcopy(responseExpectations.get(HEADERS)); // change a header to force an error final String headerName = (String) headersCopy.keySet().toArray()[0]; headersCopy.put(headerName, headersCopy.get(headerName) + "junk"); final Map response = new HashMap<>(); response.put(STATUS, TestingFramework.ALREADY_CHECKED); response.put(BODY, TestingFramework.ALREADY_CHECKED); // return different headers than expected. response.put(HEADERS, headersCopy); return response; } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } private Object deepcopy(final Object obj) { try { return TestingFramework.deepcopy(obj); } catch (final Exception e) { Assertions.fail("deepcopy failed: " + e.getMessage()); return null; } } @Test public void requestMethodUnexpected() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { // change the request from what is expected. Assertions.assertEquals("GET", request.get(METHOD)); request.put(METHOD, "POST"); return super.execute(defaultURI, request, requestHandler, responseExpectations); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); final Map test = new HashMap<>(); final Map request = new HashMap<>(); test.put(REQUEST, request); request.put(NAME, "MyName"); framework.addTest(test); final TestingFrameworkException exception = Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); // make sure the HTTP Client name is in the message. final String message = exception.getMessage(); final ClientPOJOAdapter pojoAdapter = adapter.getClientPOJOAdapter(); final String httpClientName = pojoAdapter == null ? TestingFrameworkException.NO_HTTP_CLIENT : pojoAdapter.getClientName(); Assertions.assertTrue(message.contains(httpClientName), "Message should contain httpClientName of " + httpClientName + "; message=" + message); Assertions.assertTrue(message.contains("MyName"), "Message should contain the test. message=" + message); } @Test public void status201() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); final TestingFramework framework = newFrameworkAndSetAdapter(adapter); final Map test = new HashMap<>(); final Map response = new HashMap<>(); test.put(RESPONSE, response); response.put(STATUS, 201); framework.addTest(test); framework.runTests(); } @Test public void deepcopyOfTest() throws Exception { final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { Assertions.assertEquals(201, responseExpectations.get(STATUS)); return alreadyCheckedResponse(); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); final Map test = new HashMap<>(); final Map response = new HashMap<>(); test.put(RESPONSE, response); response.put(STATUS, 201); framework.addTest(test); // Make sure the framework makes a copy of the test for itself. // This put should be ignored by the framework. response.put(STATUS, 300); framework.runTests(); } @Test public void removeParameter() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { // change the request from what is expected. @SuppressWarnings("unchecked") final Map query = (Map) request.get(QUERY); Assertions.assertTrue(query.containsKey("p1")); query.remove("p1"); return super.execute(defaultURI, request, requestHandler, responseExpectations); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void changeParameter() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { // change the request from what is expected. @SuppressWarnings("unchecked") final Map query = (Map) request.get(QUERY); Assertions.assertTrue(query.containsKey("p1")); query.put("p1", query.get("p1") + "junk"); return super.execute(defaultURI, request, requestHandler, responseExpectations); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void removeHeader() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { // change the request from what is expected. @SuppressWarnings("unchecked") final Map headers = (Map) request.get(HEADERS); Assertions.assertTrue(headers.containsKey("header1")); headers.remove("header1"); return super.execute(defaultURI, request, requestHandler, responseExpectations); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void changeHeader() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { // change the request from what is expected. @SuppressWarnings("unchecked") final Map headers = (Map) request.get(HEADERS); Assertions.assertTrue(headers.containsKey("header1")); headers.put("header1", headers.get("header1") + "junk"); return super.execute(defaultURI, request, requestHandler, responseExpectations); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void changeBody() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { // change the request from what is expected. final String body = (String) request.get(BODY); Assertions.assertNotNull(body); request.put(BODY, request.get(BODY) + "junk"); return super.execute(defaultURI, request, requestHandler, responseExpectations); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void changeContentType() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { // change the request from what is expected. final String contentType = (String) request.get(CONTENT_TYPE); Assertions.assertNotNull(contentType); request.put(CONTENT_TYPE, request.get(CONTENT_TYPE) + "junk"); return super.execute(defaultURI, request, requestHandler, responseExpectations); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void changeProtocolVersion() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { // change the request from what is expected. final ProtocolVersion protocolVersion = (ProtocolVersion) request.get(PROTOCOL_VERSION); Assertions.assertNotNull(protocolVersion); request.put(PROTOCOL_VERSION, HttpVersion.HTTP_1_0); return super.execute(defaultURI, request, requestHandler, responseExpectations); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void changeResponseExpectationsFails() throws Exception { final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { /* * The adapter should change the responseExpectations in the modifyResponseExpectations() * method before they are sent to the request handler. The expectations should not * be changed here. */ // the next command should throw. responseExpectations.put(STATUS, 201); return null; } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); Assertions.assertThrows(TestingFrameworkException.class, () -> framework.runTests()); } @Test public void changeResponseStatus() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { // change the responseExpectations from what is expected. The change should be ignored // by the request handler, and a 200 should actually be returned. Assertions.assertEquals(200, responseExpectations.get(STATUS)); // The next line is needed because we have to make a copy of the responseExpectations. // It is an unmodifiable map. final Map tempResponseExpectations = new HashMap<>(responseExpectations); tempResponseExpectations.put(STATUS, 201); final Map response = super.execute(defaultURI, request, requestHandler, tempResponseExpectations); Assertions.assertEquals(200, response.get(STATUS)); return response; } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); framework.runTests(); } @Test public void modifyRequestCalled() throws Exception { final TestingFrameworkRequestHandler mockRequestHandler = Mockito.mock(TestingFrameworkRequestHandler.class); final String UNLIKELY_ITEM = "something_unlikely_to_be_in_a_real_request"; final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { // make sure the modifyRequest method was called by seeing if the request was modified. Assertions.assertTrue(request.containsKey(UNLIKELY_ITEM), "modifyRequest should have been called."); final Map response = new HashMap<>(); response.put(STATUS, responseExpectations.get(STATUS)); response.put(BODY, responseExpectations.get(BODY)); response.put(CONTENT_TYPE, responseExpectations.get(CONTENT_TYPE)); response.put(HEADERS, responseExpectations.get(HEADERS)); return response; } @Override public Map modifyRequest(final Map request) { // let the adapter change the request if needed. request.put(UNLIKELY_ITEM, new Object()); return super.modifyRequest(request); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.setRequestHandler(mockRequestHandler); framework.addTest(); framework.runTests(); // assertNothingThrown() should have been called. Mockito.verify(mockRequestHandler).assertNothingThrown(); } @Test public void modifyResponseExpectationsCalled() throws Exception { final TestingFrameworkRequestHandler mockRequestHandler = Mockito.mock(TestingFrameworkRequestHandler.class); final String UNLIKELY_ITEM = "something_unlikely_to_be_in_a_real_response"; final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { // make sure the modifyRequest method was called by seeing if the request was modified. Assertions.assertTrue(responseExpectations.containsKey(UNLIKELY_ITEM), "modifyResponseExpectations should have been called."); final Map response = new HashMap<>(); response.put(STATUS, responseExpectations.get(STATUS)); response.put(BODY, responseExpectations.get(BODY)); response.put(CONTENT_TYPE, responseExpectations.get(CONTENT_TYPE)); response.put(HEADERS, responseExpectations.get(HEADERS)); return response; } @Override public Map modifyResponseExpectations( final Map request, final Map responseExpectations) { // let the adapter change the request if needed. responseExpectations.put(UNLIKELY_ITEM, new Object()); return super.modifyResponseExpectations(request, responseExpectations); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.setRequestHandler(mockRequestHandler); framework.addTest(); framework.runTests(); // assertNothingThrown() should have been called. Mockito.verify(mockRequestHandler).assertNothingThrown(); } @Test public void adapterDoesNotSupport() throws Exception { final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { Assertions.fail("callMethod should not have been called"); return null; } @Override public boolean isRequestSupported(final Map request) { return false; } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); framework.addTest(); framework.runTests(); } @Test public void defaultTestsWithMockedAdapter() throws Exception { final Set calledMethodSet = new HashSet<>(); final ClientTestingAdapter adapter = new ClientTestingAdapter() { @Override public Map execute( final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { calledMethodSet.add((String) request.get(METHOD)); return alreadyCheckedResponse(); } }; // create the framework without deleting the default tests. final TestingFramework framework = new TestingFramework(); framework.setAdapter(adapter); framework.runTests(); for (final String method : TestingFramework.ALL_METHODS) { Assertions.assertTrue(calledMethodSet.contains(method), "Method not in default tests. method=" + method); } } @Test public void defaultTests() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter(); // create the framework without deleting the default tests. final TestingFramework framework = new TestingFramework(); framework.setAdapter(adapter); framework.runTests(); } @Test public void addTestNoMocks() throws TestingFrameworkException { final TestingFramework framework = new TestingFramework(new ClassicTestClientTestingAdapter()); // The following test could be constructed in Groovy/Spock like this: // // HttpServerTestingFramework.ALL_METHODS.each { method -> // framework.addTest( // request: [ // path: '/stuff', // method:method, // query: [param : 'something'], // headers: [header1:'stuff', header2:'more-stuff'], // contentType: 'text/plain; charset=us-ascii', // body: 'What is the meaning of life?', // ], // response: [ // status:201, // headers: [header3:'header_stuff',], // contentType: 'text/html; charset=us-ascii', // body: responseBody, // ], // ) final Map test = new HashMap<>(); // Add request. final Map request = new HashMap<>(); test.put(REQUEST, request); request.put(PATH, "stuff"); final Map queryMap = new HashMap<>(); request.put(QUERY, queryMap); queryMap.put("param", "something"); final Map requestHeadersMap = new HashMap<>(); request.put(HEADERS, requestHeadersMap); requestHeadersMap.put("header1", "stuff"); requestHeadersMap.put("header2", "more-stuff"); request.put(CONTENT_TYPE, "text/plain; charset=us-ascii"); request.put(BODY, "What is the meaning of life?"); // Response final Map response = new HashMap<>(); test.put(RESPONSE, response); response.put(STATUS, 201); final Map responseHeadersMap = new HashMap<>(); response.put(HEADERS, responseHeadersMap); responseHeadersMap.put("header3", "header_stuff"); response.put(CONTENT_TYPE, "text/html; charset=us-ascii"); response.put(BODY, "42"); for (final String method : TestingFramework.ALL_METHODS) { request.put(METHOD, method); framework.addTest(test); } framework.runTests(); } @Test public void nulls() throws TestingFrameworkException { final TestingFramework framework = new TestingFramework(new ClassicTestClientTestingAdapter()); // The following test could be constructed in Groovy/Spock like this: // // WebServerTestingFramework.ALL_METHODS.each { method -> // framework.addTest( // request: [ // path: null, // method:method, // query: null, // headers: null, // contentType: null, // body: null, // ], // response: [ // status:null, // headers: null, // contentType: null, // body: null, // ], // ) final Map test = new HashMap<>(); // Add request. final Map request = new HashMap<>(); test.put(REQUEST, request); request.put(PATH, null); request.put(QUERY, null); request.put(HEADERS, null); request.put(CONTENT_TYPE, null); request.put(BODY, null); // Response final Map response = new HashMap<>(); test.put(RESPONSE, response); response.put(STATUS, null); response.put(HEADERS, null); response.put(CONTENT_TYPE, null); response.put(BODY, null); for (final String method : TestingFramework.ALL_METHODS) { request.put(METHOD, method); framework.addTest(test); } framework.runTests(); } @Test public void parameterInPath() throws Exception { final ClientTestingAdapter adapter = new ClassicTestClientTestingAdapter() { @Override public Map execute(final String defaultURI, final Map request, final TestingFrameworkRequestHandler requestHandler, final Map responseExpectations) throws TestingFrameworkException { @SuppressWarnings("unchecked") final Map query = (Map) request.get(QUERY); Assertions.assertTrue(query.containsKey("stuffParm"), "Parameters appended to the path should have been put in the query."); Assertions.assertTrue(query.containsKey("stuffParm2")); Assertions.assertEquals("stuff", query.get("stuffParm")); Assertions.assertEquals("stuff2", query.get("stuffParm2")); Assertions.assertEquals("/stuff", request.get(PATH)); return alreadyCheckedResponse(); } }; final TestingFramework framework = newFrameworkAndSetAdapter(adapter); final Map test = new HashMap<>(); // Add request. final Map request = new HashMap<>(); test.put(REQUEST, request); request.put(PATH, "/stuff?stuffParm=stuff&stuffParm2=stuff2"); framework.addTest(test); framework.runTests(); } }httpcore5-testing/src/test/java/org/apache/hc/core5/testing/framework/TestClientPojoAdapter.java0100664 0000000 0000000 00000005430 14403631147 032036 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.framework; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestClientPojoAdapter { @Test public void modifyRequest() throws Exception { final ClientPOJOAdapter adapter = new ClassicTestClientAdapter(); final Map request = new HashMap<>(); final Map request2 = adapter.modifyRequest(request); Assertions.assertSame(request, request2, "request should have been returned"); } @Test public void checkRequestSupport() throws Exception { final ClientPOJOAdapter adapter = new ClassicTestClientAdapter(); final String reason = adapter.checkRequestSupport(null); Assertions.assertNull(reason, "reason should be null"); adapter.assertRequestSupported(null); } @Test public void checkRequestSupportThrows() throws Exception { final ClientPOJOAdapter adapter = new ClientPOJOAdapter() { @Override public Map execute(final String defaultURI, final Map request) throws Exception { return null; } @Override public String checkRequestSupport(final java.util.Map request) { return "A reason this request is not supported."; } @Override public String getClientName() { return null; } }; Assertions.assertThrows(Exception.class, () -> adapter.assertRequestSupported(null)); } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/extension/0040775 0000000 0000000 00000000000 14403631147 025004 5ustar000000000 0000000 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/extension/SocksProxyResource.java0100664 0000000 0000000 00000004615 14403631147 031506 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing.extension; import org.apache.hc.core5.testing.SocksProxy; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SocksProxyResource implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(SocksProxyResource.class); private SocksProxy proxy; @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up SOCKS proxy"); proxy = new SocksProxy(); proxy.start(); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down SOCKS proxy"); if (proxy != null) { try { proxy.shutdown(TimeValue.ofSeconds(5)); } catch (final Exception ignore) { } } } public SocksProxy proxy() { Assertions.assertNotNull(proxy); return proxy; } } httpcore5-testing/src/test/java/org/apache/hc/core5/testing/SSLTestContexts.java0100664 0000000 0000000 00000004340 14245617503 026666 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.testing; import java.net.URL; import javax.net.ssl.SSLContext; import org.apache.hc.core5.ssl.SSLContextBuilder; public final class SSLTestContexts { public static SSLContext createServerSSLContext() throws Exception { final URL keyStoreURL = SSLTestContexts.class.getResource("/test.p12"); final String storePassword = "nopassword"; return SSLContextBuilder.create() .setKeyStoreType("pkcs12") .loadTrustMaterial(keyStoreURL, storePassword.toCharArray()) .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray()) .build(); } public static SSLContext createClientSSLContext() throws Exception { final URL keyStoreURL = SSLTestContexts.class.getResource("/test.p12"); final String storePassword = "nopassword"; return SSLContextBuilder.create() .setKeyStoreType("pkcs12") .loadTrustMaterial(keyStoreURL, storePassword.toCharArray()) .build(); } } httpcore5-testing/docker-compose.yml0100664 0000000 0000000 00000002221 14403631147 016634 0ustar000000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. version: '3.5' services: test-httpd: container_name: "my-hc-tests-httpd" image: "hc-tests-httpd:latest" ports: - "8080:80" test-nginx: container_name: "my-hc-tests-nginx" image: "hc-tests-nginx:latest" ports: - "8081:80" test-httpbin: container_name: "my-hc-tests-httpbin" image: "hc-tests-httpbin:latest" ports: - "8082:80" httpcore5-testing/pom.xml0100664 0000000 0000000 00000014123 14443065161 014521 0ustar000000000 0000000 4.0.0 org.apache.httpcomponents.core5 httpcore5-parent 5.2.2 httpcore5-testing Apache HttpComponents Core Integration Tests Apache HttpComponents HTTP/2 and HTTP/1.1 core component integration tests org.apache.httpcomponents.core5.httpcore5.testing org.apache.httpcomponents.core5 httpcore5 org.apache.httpcomponents.core5 httpcore5-h2 org.apache.httpcomponents.core5 httpcore5-reactive commons-cli commons-cli 1.5.0 org.slf4j slf4j-api io.reactivex.rxjava2 rxjava ${rxjava.version} io.reactivex.rxjava3 rxjava ${rxjava3.version} org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.conscrypt conscrypt-openjdk-uber test org.junit.jupiter junit-jupiter test org.hamcrest hamcrest test org.mockito mockito-core test docker org.apache.maven.plugins maven-failsafe-plugin ${hc.surefire.version} integration-test verify io.fabric8 docker-maven-plugin 0.31.0 start-httpbin pre-integration-test start kennethreitz/httpbin httpbin MAGENTA 8082:80 stop-test-servers post-integration-test stop maven-project-info-reports-plugin false index dependencies dependency-info summary httpcore5-testing/docker/0040775 0000000 0000000 00000000000 14403631147 014454 5ustar000000000 0000000 httpcore5-testing/docker/nginx/0040775 0000000 0000000 00000000000 14323605260 015575 5ustar000000000 0000000 httpcore5-testing/docker/nginx/Dockerfile0100664 0000000 0000000 00000002255 14323605260 017570 0ustar000000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM nginx:1.22 MAINTAINER dev@hc.apache.org ENV var_dir /var/nginx ENV www_dir ${var_dir}/www RUN apt-get update RUN apt-get install -y subversion RUN mkdir -p ${var_dir} RUN svn co --depth immediates http://svn.apache.org/repos/asf/httpcomponents/site ${www_dir} RUN svn up --set-depth infinity ${www_dir}/images RUN svn up --set-depth infinity ${www_dir}/css COPY default.conf /etc/nginx/conf.d/default.conf httpcore5-testing/docker/nginx/default.conf0100664 0000000 0000000 00000002512 14245617503 020073 0ustar000000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. server { listen 80 http2; server_name localhost; location / { root /var/nginx/www; index index.html; location = /index.html { http2_push "/css/site.css"; http2_push "/css/maven-theme.css"; http2_push "/css/maven-base.css"; http2_push "/css/hc-maven.css"; http2_push "/images/logos/httpcomponents.png"; } } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } httpcore5-testing/docker/httpbin/0040775 0000000 0000000 00000000000 14245617503 016130 5ustar000000000 0000000 httpcore5-testing/docker/httpbin/Dockerfile0100664 0000000 0000000 00000001514 14245617503 020120 0ustar000000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM kennethreitz/httpbin:latest MAINTAINER dev@hc.apache.org httpcore5-testing/docker/apache-httpd/0040775 0000000 0000000 00000000000 14245617503 017022 5ustar000000000 0000000 httpcore5-testing/docker/apache-httpd/httpd-vhosts.conf0100664 0000000 0000000 00000002762 14245617503 022344 0ustar000000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ServerName localhost:80 Protocols h2c http/1.1 LogLevel http2:info H2Push on AllowOverride none Require all denied DocumentRoot "/var/httpd/www" Options Indexes FollowSymLinks AllowOverride None Require all granted Header add Link ";rel=preload" Header add Link ";rel=preload" Header add Link ";rel=preload" Header add Link ";rel=preload" Header add Link ";rel=preload" httpcore5-testing/docker/apache-httpd/Dockerfile0100664 0000000 0000000 00000002721 14245617503 021013 0ustar000000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM httpd:2.4 MAINTAINER dev@hc.apache.org ENV var_dir /var/httpd ENV www_dir ${var_dir}/www RUN apt-get update RUN apt-get install -y subversion RUN mkdir -p ${var_dir} RUN svn co --depth immediates http://svn.apache.org/repos/asf/httpcomponents/site ${www_dir} RUN svn up --set-depth infinity ${www_dir}/images RUN svn up --set-depth infinity ${www_dir}/css RUN sed -i -E 's/^\s*#\s*(LoadModule\s+http2_module\s+modules\/mod_http2.so)/\1/' conf/httpd.conf RUN sed -i -E 's/^\s*ServerAdmin.*$/ServerAdmin dev@hc.apache.org/' conf/httpd.conf RUN sed -i -E 's/^\s*#\s*(Include\s+conf\/extra\/httpd-vhosts.conf)/\1/' conf/httpd.conf COPY httpd-vhosts.conf /usr/local/apache2/conf/extra/httpd-vhosts.confhttpcore5-testing/docker/BUILDING.txt0100664 0000000 0000000 00000001106 14403631147 016405 0ustar000000000 0000000 Building Docker containers for compatibility tests ======================================================== Remark: omit sudo command if executing as root = Build Apache HTTPD image --- sudo docker build -t hc-tests-httpd apache-httpd --- = Build Nginx image --- sudo docker build -t hc-tests-nginx nginx --- = Build HTTPBIN image --- sudo docker build -t hc-tests-httpbin httpbin --- = Start containers --- sudo docker-compose up --- = Execute H2 compatibility tests --- H2CompatibilityTest http://localhost:8080 APACHE-HTTPD H2CompatibilityTest http://localhost:8081 NGINXBUILDING.txt0100664 0000000 0000000 00000001705 14245617503 011557 0ustar000000000 0000000 Building HttpComponents Core ============================ (1) Requisites -------------- JDK 1.7+ is required in order to compile and run HttpCore. HttpCore utilizes Maven as a distribution management and packaging tool. Version 3.3 or later is required. Maven installation and configuration instructions can be found here: http://maven.apache.org/run-maven/index.html (2) Executing test cases Execute the following command in order to compile and test the components mvn test (3) Building packages Execute the following command in order to build the JAR packages and install them to the local repository: mvn install The JAR packages can be found in the target folders of their respective modules httpcore5/target/httpcore5-.jar httpcore5-h2/target/httpcore5-h2-.jar httpcore5-testing/target/httpcore5-testing-.jar httpcore5-osgi/target/org.apache.httpcomponents.httpcore_.jar where is the release version doap_HttpComponents_Core.rdf0100664 0000000 0000000 00000011336 14245617503 015257 0ustar000000000 0000000 2007-11-15 Apache HttpComponents Core HTTP transport library including support for asynchronous execution based on Java NIO. HttpCore is a set of low level HTTP transport components that can be used to build custom client and server side HTTP services with a minimal footprint. HttpCore supports two I/O models: blocking I/O model based on the classic Java I/O and non-blocking, event driven I/O model based on Java NIO. Java Hypertext Transfer Protocol Version 2 (HTTP/2) IETF RFC 7540 HPACK: Header Compression for HTTP/2 IETF RFC 7541 Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing IETF RFC 7230 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content IETF RFC 7231 Hypertext Transfer Protocol -- HTTP/1.0 IETF RFC 1945 httpcore5-h2/0040775 0000000 0000000 00000000000 14443065100 012033 5ustar000000000 0000000 httpcore5-h2/src/0040775 0000000 0000000 00000000000 14245617503 012634 5ustar000000000 0000000 httpcore5-h2/src/main/0040775 0000000 0000000 00000000000 14245617503 013560 5ustar000000000 0000000 httpcore5-h2/src/main/java/0040775 0000000 0000000 00000000000 14245617503 014501 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/0040775 0000000 0000000 00000000000 14245617503 015270 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/0040775 0000000 0000000 00000000000 14245617503 016511 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/0040775 0000000 0000000 00000000000 14245617503 017103 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/0040775 0000000 0000000 00000000000 14245617503 020120 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/0040775 0000000 0000000 00000000000 14403631147 021155 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/package-info.java0100664 0000000 0000000 00000002365 14245617503 024353 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core HTTP/2 transport component APIs. */ package org.apache.hc.core5.http2; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/config/0040775 0000000 0000000 00000000000 14403631147 022422 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/config/package-info.java0100664 0000000 0000000 00000002366 14245617503 025621 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core HTTP/2 configuration APIs. */ package org.apache.hc.core5.http2.config; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/config/H2Config.java0100664 0000000 0000000 00000017461 14403631147 024672 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.config; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http2.frame.FrameConsts; import org.apache.hc.core5.util.Args; /** * HTTP/2 protocol configuration. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class H2Config { public static final H2Config DEFAULT = custom().build(); public static final H2Config INIT = initial().build(); private final int headerTableSize; private final boolean pushEnabled; private final int maxConcurrentStreams; private final int initialWindowSize; private final int maxFrameSize; private final int maxHeaderListSize; private final boolean compressionEnabled; H2Config(final int headerTableSize, final boolean pushEnabled, final int maxConcurrentStreams, final int initialWindowSize, final int maxFrameSize, final int maxHeaderListSize, final boolean compressionEnabled) { super(); this.headerTableSize = headerTableSize; this.pushEnabled = pushEnabled; this.maxConcurrentStreams = maxConcurrentStreams; this.initialWindowSize = initialWindowSize; this.maxFrameSize = maxFrameSize; this.maxHeaderListSize = maxHeaderListSize; this.compressionEnabled = compressionEnabled; } public int getHeaderTableSize() { return headerTableSize; } public boolean isPushEnabled() { return pushEnabled; } public int getMaxConcurrentStreams() { return maxConcurrentStreams; } public int getInitialWindowSize() { return initialWindowSize; } public int getMaxFrameSize() { return maxFrameSize; } public int getMaxHeaderListSize() { return maxHeaderListSize; } public boolean isCompressionEnabled() { return compressionEnabled; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("[headerTableSize=").append(this.headerTableSize) .append(", pushEnabled=").append(this.pushEnabled) .append(", maxConcurrentStreams=").append(this.maxConcurrentStreams) .append(", initialWindowSize=").append(this.initialWindowSize) .append(", maxFrameSize=").append(this.maxFrameSize) .append(", maxHeaderListSize=").append(this.maxHeaderListSize) .append(", compressionEnabled=").append(this.compressionEnabled) .append("]"); return builder.toString(); } public static H2Config.Builder custom() { return new Builder(); } private static final int INIT_HEADER_TABLE_SIZE = 4096; private static final boolean INIT_ENABLE_PUSH = true; private static final int INIT_MAX_FRAME_SIZE = FrameConsts.MIN_FRAME_SIZE; private static final int INIT_WINDOW_SIZE = 65535; private static final int INIT_CONCURRENT_STREAM = 250; public static H2Config.Builder initial() { return new Builder() .setHeaderTableSize(INIT_HEADER_TABLE_SIZE) .setPushEnabled(INIT_ENABLE_PUSH) .setMaxConcurrentStreams(Integer.MAX_VALUE) // no limit .setMaxFrameSize(INIT_MAX_FRAME_SIZE) .setInitialWindowSize(INIT_WINDOW_SIZE) .setMaxHeaderListSize(Integer.MAX_VALUE); // unlimited } public static H2Config.Builder copy(final H2Config config) { Args.notNull(config, "Connection config"); return new Builder() .setHeaderTableSize(config.getHeaderTableSize()) .setPushEnabled(config.isPushEnabled()) .setMaxConcurrentStreams(config.getMaxConcurrentStreams()) .setInitialWindowSize(config.getInitialWindowSize()) .setMaxFrameSize(config.getMaxFrameSize()) .setMaxHeaderListSize(config.getMaxHeaderListSize()) .setCompressionEnabled(config.isCompressionEnabled()); } public static class Builder { private int headerTableSize; private boolean pushEnabled; private int maxConcurrentStreams; private int initialWindowSize; private int maxFrameSize; private int maxHeaderListSize; private boolean compressionEnabled; Builder() { this.headerTableSize = INIT_HEADER_TABLE_SIZE * 2; this.pushEnabled = INIT_ENABLE_PUSH; this.maxConcurrentStreams = INIT_CONCURRENT_STREAM; this.initialWindowSize = INIT_WINDOW_SIZE; this.maxFrameSize = FrameConsts.MIN_FRAME_SIZE * 4; this.maxHeaderListSize = FrameConsts.MAX_FRAME_SIZE; this.compressionEnabled = true; } public Builder setHeaderTableSize(final int headerTableSize) { Args.notNegative(headerTableSize, "Header table size"); this.headerTableSize = headerTableSize; return this; } public Builder setPushEnabled(final boolean pushEnabled) { this.pushEnabled = pushEnabled; return this; } public Builder setMaxConcurrentStreams(final int maxConcurrentStreams) { Args.positive(maxConcurrentStreams, "Max concurrent streams"); this.maxConcurrentStreams = maxConcurrentStreams; return this; } public Builder setInitialWindowSize(final int initialWindowSize) { Args.positive(initialWindowSize, "Initial window size"); this.initialWindowSize = initialWindowSize; return this; } public Builder setMaxFrameSize(final int maxFrameSize) { this.maxFrameSize = Args.checkRange(maxFrameSize, FrameConsts.MIN_FRAME_SIZE, FrameConsts.MAX_FRAME_SIZE, "Invalid max frame size"); return this; } public Builder setMaxHeaderListSize(final int maxHeaderListSize) { Args.positive(maxHeaderListSize, "Max header list size"); this.maxHeaderListSize = maxHeaderListSize; return this; } public Builder setCompressionEnabled(final boolean compressionEnabled) { this.compressionEnabled = compressionEnabled; return this; } public H2Config build() { return new H2Config( headerTableSize, pushEnabled, maxConcurrentStreams, initialWindowSize, maxFrameSize, maxHeaderListSize, compressionEnabled); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/config/H2Param.java0100664 0000000 0000000 00000004305 14245617503 024522 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.config; /** * HTTP/2 protocol parameters. * * @since 5.0 */ public enum H2Param { HEADER_TABLE_SIZE (0x1), ENABLE_PUSH (0x2), MAX_CONCURRENT_STREAMS (0x3), INITIAL_WINDOW_SIZE (0x4), MAX_FRAME_SIZE (0x5), MAX_HEADER_LIST_SIZE (0x6); int code; H2Param(final int code) { this.code = code; } public int getCode() { return code; } private static final H2Param[] LOOKUP_TABLE = new H2Param[6]; static { for (final H2Param param: H2Param.values()) { LOOKUP_TABLE[param.code - 1] = param; } } public static H2Param valueOf(final int code) { if (code < 1 || code > LOOKUP_TABLE.length) { return null; } return LOOKUP_TABLE[code - 1]; } public static String toString(final int code) { if (code < 1 || code > LOOKUP_TABLE.length) { return Integer.toString(code); } return LOOKUP_TABLE[code - 1].name(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/config/H2Setting.java0100664 0000000 0000000 00000003654 14245617503 025105 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.config; import org.apache.hc.core5.util.Args; /** * HTTP/2 protocol settings. * * @since 5.0 */ public final class H2Setting { private final H2Param param; private final int value; public H2Setting(final H2Param param, final int value) { Args.notNull(param, "Setting parameter"); Args.notNegative(value, "Setting value must be a non-negative value"); this.param = param; this.value = value; } public int getCode() { return param.code; } public int getValue() { return value; } @Override public String toString() { final StringBuilder sb = new StringBuilder().append(param).append(": ").append(value); return sb.toString(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/frame/0040775 0000000 0000000 00000000000 14245617503 022253 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/frame/package-info.java0100664 0000000 0000000 00000002362 14245617503 025442 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Core HTTP/2 data frame APIs. */ package org.apache.hc.core5.http2.frame; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/frame/FrameConsts.java0100664 0000000 0000000 00000003150 14245617503 025336 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; /** * Values used by HTTP/2 protocol. * * @since 5.0 */ public final class FrameConsts { private FrameConsts() { // Do not allow utility class to be instantiated. } public final static int HEAD_LEN = 9; public final static int MAX_PADDING = 255; public final static int MIN_FRAME_SIZE = 16384; // 2 ^ 14 public final static int MAX_FRAME_SIZE = 16777215; // 2 ^ 24 - 1; } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/frame/StreamIdGenerator.java0100664 0000000 0000000 00000004213 14245617503 026472 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; /** * HTTP/2 stream ID generator. * * @since 5.0 */ public interface StreamIdGenerator { int generate(int previousMax); boolean isSameSide(int streamId); StreamIdGenerator ODD = new StreamIdGenerator() { @Override public int generate(final int previousMax) { int i = previousMax + 1; if (i % 2 == 0) { i++; } return i; } @Override public boolean isSameSide(final int streamId) { return (streamId & 1) == 1; } }; StreamIdGenerator EVEN = new StreamIdGenerator() { @Override public int generate(final int previousMax) { int i = previousMax + 1; if (i % 2 == 1) { i++; } return i; } @Override public boolean isSameSide(final int streamId) { return (streamId & 1) == 0; } }; } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/frame/FramePrinter.java0100664 0000000 0000000 00000022460 14245617503 025515 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Objects; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.Chars; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.config.H2Param; @Internal public final class FramePrinter { public void printFrameInfo(final RawFrame frame, final Appendable appendable) throws IOException { appendable.append("stream ").append(Integer.toString(frame.getStreamId())).append(" frame: "); final FrameType type = FrameType.valueOf(frame.getType()); appendable.append(Objects.toString(type)) .append(" (0x").append(Integer.toHexString(frame.getType())).append("); flags: "); final int flags = frame.getFlags(); if (flags > 0) { switch (type) { case SETTINGS: case PING: if ((flags & FrameFlag.ACK.value) > 0) { appendable.append(FrameFlag.ACK.name()).append(" "); } break; case DATA: if ((flags & FrameFlag.END_STREAM.value) > 0) { appendable.append(FrameFlag.END_STREAM.name()).append(" "); } if ((flags & FrameFlag.PADDED.value) > 0) { appendable.append(FrameFlag.PADDED.name()).append(" "); } break; case HEADERS: if ((flags & FrameFlag.END_STREAM.value) > 0) { appendable.append(FrameFlag.END_STREAM.name()).append(" "); } if ((flags & FrameFlag.END_HEADERS.value) > 0) { appendable.append(FrameFlag.END_HEADERS.name()).append(" "); } if ((flags & FrameFlag.PADDED.value) > 0) { appendable.append(FrameFlag.PADDED.name()).append(" "); } if ((flags & FrameFlag.PRIORITY.value) > 0) { appendable.append(FrameFlag.PRIORITY.name()).append(" "); } break; case PUSH_PROMISE: if ((flags & FrameFlag.END_HEADERS.value) > 0) { appendable.append(FrameFlag.END_HEADERS.name()).append(" "); } if ((flags & FrameFlag.PADDED.value) > 0) { appendable.append(FrameFlag.PADDED.name()).append(" "); } break; case CONTINUATION: if ((flags & FrameFlag.END_HEADERS.value) > 0) { appendable.append(FrameFlag.END_HEADERS.name()).append(" "); } } } appendable.append("(0x").append(Integer.toHexString(flags)).append("); length: ").append(Integer.toString(frame.getLength())); } public void printPayload(final RawFrame frame, final Appendable appendable) throws IOException { final FrameType type = FrameType.valueOf(frame.getType()); final ByteBuffer buf = frame.getPayloadContent(); if (buf != null) { switch (type) { case SETTINGS: if ((buf.remaining() % 6) == 0) { while (buf.hasRemaining()) { final int code = buf.getShort(); final H2Param param = H2Param.valueOf(code); final int value = buf.getInt(); if (param != null) { appendable.append(param.name()); } else { appendable.append("0x").append(Integer.toHexString(code)); } appendable.append(": ").append(Integer.toString(value)).append("\r\n"); } } else { appendable.append("Invalid\r\n"); } break; case RST_STREAM: if (buf.remaining() == 4) { appendable.append("Code "); final int code = buf.getInt(); final H2Error error = H2Error.getByCode(code); if (error != null) { appendable.append(error.name()); } else { appendable.append("0x").append(Integer.toHexString(code)); } appendable.append("\r\n"); } else { appendable.append("Invalid\r\n"); } break; case GOAWAY: if (buf.remaining() >= 8) { final int lastStream = buf.getInt(); appendable.append("Last stream ").append(Integer.toString(lastStream)).append("\r\n"); appendable.append("Code "); final int code2 = buf.getInt(); final H2Error error2 = H2Error.getByCode(code2); if (error2 != null) { appendable.append(error2.name()); } else { appendable.append("0x").append(Integer.toHexString(code2)); } appendable.append("\r\n"); final byte[] tmp = new byte[buf.remaining()]; buf.get(tmp); appendable.append(new String(tmp, StandardCharsets.US_ASCII)); appendable.append("\r\n"); } else { appendable.append("Invalid\r\n"); } break; case WINDOW_UPDATE: if (buf.remaining() == 4) { final int increment = buf.getInt(); appendable.append("Increment ").append(Integer.toString(increment)).append("\r\n"); } else { appendable.append("Invalid\r\n"); } break; case PUSH_PROMISE: if (buf.remaining() > 4) { final int streamId = buf.getInt(); appendable.append("Promised stream ").append(Integer.toString(streamId)).append("\r\n"); printData(buf, appendable); } else { appendable.append("Invalid\r\n"); } break; default: printData(frame.getPayload(), appendable); } } } public void printData(final ByteBuffer data, final Appendable appendable) throws IOException { final ByteBuffer buf = data.duplicate(); final byte[] line = new byte[16]; while (buf.hasRemaining()) { final int chunk = Math.min(buf.remaining(), line.length); buf.get(line, 0, chunk); for (int i = 0; i < chunk; i++) { final char ch = (char) line[i]; if (ch > Chars.SP && ch <= Chars.DEL) { appendable.append(ch); } else if (Character.isWhitespace(ch)) { appendable.append(' '); } else { appendable.append('.'); } } for (int i = chunk; i < 17; i++) { appendable.append(' '); } for (int i = 0; i < chunk; i++) { appendable.append(' '); final int b = line[i] & 0xff; final String s = Integer.toHexString(b); if (s.length() == 1) { appendable.append("0"); } appendable.append(s); } appendable.append("\r\n"); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/frame/RawFrame.java0100664 0000000 0000000 00000004717 14245617503 024630 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; import java.nio.ByteBuffer; /** * HTTP/2 stream frame. * * @since 5.0 */ public final class RawFrame extends Frame { private final ByteBuffer payload; private final int len; public RawFrame(final int type, final int flags, final int streamId, final ByteBuffer payload) { super(type, flags, streamId); this.payload = payload; this.len = payload != null ? payload.remaining() : 0; } public boolean isPadded() { return isFlagSet(FrameFlag.PADDED); } public int getLength() { return len; } public ByteBuffer getPayloadContent() { if (payload != null) { if (isPadded()) { final ByteBuffer dup = payload.duplicate(); if (dup.remaining() == 0) { return null; } final int padding = dup.get() & 0xff; if (padding > dup.remaining()) { return null; } dup.limit(dup.limit() - padding); return dup; } return payload.duplicate(); } return null; } @Override public ByteBuffer getPayload() { return payload != null ? payload.duplicate() : null; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/frame/DefaultFrameFactory.java0100664 0000000 0000000 00000005551 14245617503 027010 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; import java.nio.ByteBuffer; import org.apache.hc.core5.util.Args; /** * Default {@link FrameFactory} implementation. * * @since 5.0 */ public class DefaultFrameFactory extends FrameFactory { public static final FrameFactory INSTANCE = new DefaultFrameFactory(); @Override public RawFrame createHeaders(final int streamId, final ByteBuffer payload, final boolean endHeaders, final boolean endStream) { Args.positive(streamId, "Stream id"); final int flags = (endHeaders ? FrameFlag.END_HEADERS.value : 0) | (endStream ? FrameFlag.END_STREAM.value : 0); return new RawFrame(FrameType.HEADERS.getValue(), flags, streamId, payload); } @Override public RawFrame createContinuation(final int streamId, final ByteBuffer payload, final boolean endHeaders) { Args.positive(streamId, "Stream id"); final int flags = (endHeaders ? FrameFlag.END_HEADERS.value : 0); return new RawFrame(FrameType.CONTINUATION.getValue(), flags, streamId, payload); } @Override public RawFrame createPushPromise(final int streamId, final ByteBuffer payload, final boolean endHeaders) { Args.positive(streamId, "Stream id"); final int flags = (endHeaders ? FrameFlag.END_HEADERS.value : 0); return new RawFrame(FrameType.PUSH_PROMISE.getValue(), flags, streamId, payload); } @Override public RawFrame createData(final int streamId, final ByteBuffer payload, final boolean endStream) { Args.positive(streamId, "Stream id"); final int flags = (endStream ? FrameFlag.END_STREAM.value : 0); return new RawFrame(FrameType.DATA.getValue(), flags, streamId, payload); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/frame/FrameType.java0100664 0000000 0000000 00000004436 14245617503 025016 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; /** * Standard HTTP/2 frame types. * * @since 5.0 */ public enum FrameType { DATA (0x00), HEADERS (0x01), PRIORITY (0x02), RST_STREAM (0x03), SETTINGS (0x04), PUSH_PROMISE (0x05), PING (0x06), GOAWAY (0x07), WINDOW_UPDATE (0x08), CONTINUATION (0x09); int value; FrameType(final int value) { this.value = value; } public int getValue() { return value; } private static final FrameType[] LOOKUP_TABLE = new FrameType[10]; static { for (final FrameType frameType: FrameType.values()) { LOOKUP_TABLE[frameType.value] = frameType; } } public static FrameType valueOf(final int value) { if (value < 0 || value >= LOOKUP_TABLE.length) { return null; } return LOOKUP_TABLE[value]; } public static String toString(final int value) { if (value < 0 || value >= LOOKUP_TABLE.length) { return Integer.toString(value); } return LOOKUP_TABLE[value].name(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/frame/Frame.java0100664 0000000 0000000 00000004527 14245617503 024155 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; /** * Abstract HTTP/2 data frame. * * @since 5.0 * * @param frame payload representation. */ public abstract class Frame { private final int type; private final int flags; private final int streamId; public Frame(final int type, final int flags, final int streamId) { this.type = type; this.flags = flags; this.streamId = streamId; } public boolean isType(final FrameType type) { return getType() == type.value; } public boolean isFlagSet(final FrameFlag flag) { return (getFlags() & flag.value) != 0; } public int getType() { return type; } public int getFlags() { return flags; } public int getStreamId() { return streamId; } public abstract T getPayload(); @Override public String toString() { final StringBuilder sb = new StringBuilder("["); sb.append("type=").append(type); sb.append(", flags=").append(flags); sb.append(", streamId=").append(streamId); sb.append(", payoad=").append(getPayload()); sb.append(']'); return sb.toString(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/frame/FrameFactory.java0100664 0000000 0000000 00000011075 14245617503 025501 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.config.H2Setting; import org.apache.hc.core5.util.Args; /** * Abstract {@link RawFrame} factory that supports standard * HTTP/2 {@link FrameType}s. * * @since 5.0 */ public abstract class FrameFactory { public RawFrame createSettings(final H2Setting... settings) { final ByteBuffer payload = ByteBuffer.allocate(settings.length * 12); for (final H2Setting setting: settings) { payload.putShort((short) setting.getCode()); payload.putInt(setting.getValue()); } payload.flip(); return new RawFrame(FrameType.SETTINGS.getValue(), 0, 0, payload); } public RawFrame createSettingsAck() { return new RawFrame(FrameType.SETTINGS.getValue(), FrameFlag.ACK.getValue(), 0, null); } public RawFrame createResetStream(final int streamId, final H2Error error) { Args.notNull(error, "Error"); return createResetStream(streamId, error.getCode()); } public RawFrame createResetStream(final int streamId, final int code) { Args.positive(streamId, "Stream id"); final ByteBuffer payload = ByteBuffer.allocate(4); payload.putInt(code); payload.flip(); return new RawFrame(FrameType.RST_STREAM.getValue(), 0, streamId, payload); } public RawFrame createPing(final ByteBuffer opaqueData) { Args.notNull(opaqueData, "Opaque data"); Args.check(opaqueData.remaining() == 8, "Opaque data length must be equal 8"); return new RawFrame(FrameType.PING.getValue(), 0, 0, opaqueData); } public RawFrame createPingAck(final ByteBuffer opaqueData) { Args.notNull(opaqueData, "Opaque data"); Args.check(opaqueData.remaining() == 8, "Opaque data length must be equal 8"); return new RawFrame(FrameType.PING.getValue(), FrameFlag.ACK.value, 0, opaqueData); } public RawFrame createGoAway(final int lastStream, final H2Error error, final String message) { Args.notNegative(lastStream, "Last stream id"); final byte[] debugData = message != null ? message.getBytes(StandardCharsets.US_ASCII) : null; final ByteBuffer payload = ByteBuffer.allocate(8 + (debugData != null ? debugData.length : 0)); payload.putInt(lastStream); payload.putInt(error.getCode()); if (debugData != null) { payload.put(debugData); } payload.flip(); return new RawFrame(FrameType.GOAWAY.getValue(), 0, 0, payload); } public abstract RawFrame createHeaders(int streamId, ByteBuffer payload, boolean endHeaders, boolean endStream); public abstract RawFrame createContinuation(int streamId, ByteBuffer payload, boolean endHeaders); public abstract RawFrame createPushPromise(int streamId, ByteBuffer payload, boolean endHeaders); public abstract RawFrame createData(int streamId, ByteBuffer payload, boolean endStream); public RawFrame createWindowUpdate(final int streamId, final int increment) { Args.notNegative(streamId, "Stream id"); Args.positive(increment, "Increment"); final ByteBuffer payload = ByteBuffer.allocate(4); payload.putInt(increment); payload.flip(); return new RawFrame(FrameType.WINDOW_UPDATE.getValue(), 0, streamId, payload); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/frame/FrameFlag.java0100664 0000000 0000000 00000003364 14245617503 024745 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; /** * Standard HTTP/2 frame flags. * * @since 5.0 */ public enum FrameFlag { END_STREAM (0x01), ACK (0x01), END_HEADERS (0x04), PADDED (0x08), PRIORITY (0x20); final int value; FrameFlag(final int value) { this.value = value; } public int getValue() { return value; } public static int of(final FrameFlag... flags) { int value = 0; for (final FrameFlag flag: flags) { value |= flag.value; } return value; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/H2ConnectionException.java0100664 0000000 0000000 00000003640 14245617503 026174 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2; import java.io.IOException; import org.apache.hc.core5.util.Args; /** * Signals fatal HTTP/2 protocol violation that renders the actual * HTTP/2 connection unreliable. * * @since 5.0 */ public class H2ConnectionException extends IOException { private static final long serialVersionUID = -2014204317155428658L; private final int code; public H2ConnectionException(final H2Error error, final String message) { super(message); Args.notNull(error, "H2 Error code"); this.code = error.getCode(); } public H2ConnectionException(final int code, final String message) { super(message); this.code = code; } public int getCode() { return code; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/0040775 0000000 0000000 00000000000 14403631147 022243 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/package-info.java0100664 0000000 0000000 00000002556 14245617503 025443 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP/2 HPACK APIs. *

* This Huffman codec implementation has been derived from Twitter HPack project * (https://github.com/twitter/hpack) *

*/ package org.apache.hc.core5.http2.hpack; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/OutboundDynamicTable.java0100664 0000000 0000000 00000010454 14403631147 027163 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; final class OutboundDynamicTable { private final StaticTable staticTable; private final FifoLinkedList headers; private final Map> mapByName; private int maxSize; private int currentSize; OutboundDynamicTable(final StaticTable staticTable) { this.staticTable = staticTable; this.headers = new FifoLinkedList(); this.mapByName = new HashMap<>(); this.maxSize = Integer.MAX_VALUE; this.currentSize = 0; } OutboundDynamicTable() { this(StaticTable.INSTANCE); } public int getMaxSize() { return maxSize; } public void setMaxSize(final int maxSize) { this.maxSize = maxSize; evict(); } public int getCurrentSize() { return currentSize; } int staticLength() { return staticTable.length(); } int dynamicLength() { return headers.size(); } Header getDynamicEntry(final int index) { return headers.get(index); } public int length() { return staticTable.length() + headers.size(); } public Header getHeader(final int index) { final int length = length(); Args.check(index >= 1, "index %s cannot be less then 1", index); Args.check(index <= length, "index %s cannot be greater then length %s ", index, length); return index <= staticTable.length() ? staticTable.get(index) : headers.get(index - staticTable.length() - 1); } public void add(final HPackHeader header) { final int entrySize = header.getTotalSize(); if (entrySize > this.maxSize) { clear(); this.mapByName.clear(); return; } final String key = header.getName(); final FifoLinkedList.InternalNode node = headers.addFirst(header); final LinkedList nodes = mapByName.computeIfAbsent(key, k -> new LinkedList<>()); nodes.addFirst(node); currentSize += entrySize; evict(); } private void clear() { currentSize = 0; headers.clear(); } public List getByName(final String key) { return this.mapByName.get(key); } private void evict() { while (currentSize > maxSize) { final FifoLinkedList.InternalNode node = headers.removeLast(); if (node != null) { final HPackHeader header = node.getHeader(); currentSize -= header.getTotalSize(); final String key = header.getName(); final LinkedList nodes = mapByName.get(key); if (nodes != null) { nodes.remove(node); } } else { Asserts.check(currentSize == 0, "Current table size must be zero"); break; } } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackDecoder.java0100664 0000000 0000000 00000027334 14323605260 025366 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CoderResult; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; /** * HPACK decoder. * * @since 5.0 */ @Internal public final class HPackDecoder { private static final String UNEXPECTED_EOS = "Unexpected end of HPACK data"; private static final String MAX_LIMIT_EXCEEDED = "Max integer exceeded"; private final InboundDynamicTable dynamicTable; private final ByteArrayBuffer contentBuf; private final CharsetDecoder charsetDecoder; private CharBuffer tmpBuf; private int maxTableSize; private int maxListSize; HPackDecoder(final InboundDynamicTable dynamicTable, final CharsetDecoder charsetDecoder) { this.dynamicTable = dynamicTable != null ? dynamicTable : new InboundDynamicTable(); this.contentBuf = new ByteArrayBuffer(256); this.charsetDecoder = charsetDecoder; this.maxTableSize = dynamicTable != null ? dynamicTable.getMaxSize() : Integer.MAX_VALUE; this.maxListSize = Integer.MAX_VALUE; } HPackDecoder(final InboundDynamicTable dynamicTable, final Charset charset) { this(dynamicTable, charset != null && !StandardCharsets.US_ASCII.equals(charset) ? charset.newDecoder() : null); } public HPackDecoder(final Charset charset) { this(new InboundDynamicTable(), charset); } public HPackDecoder(final CharsetDecoder charsetDecoder) { this(new InboundDynamicTable(), charsetDecoder); } static int readByte(final ByteBuffer src) throws HPackException { if (!src.hasRemaining()) { throw new HPackException(UNEXPECTED_EOS); } return src.get() & 0xff; } static int peekByte(final ByteBuffer src) throws HPackException { if (!src.hasRemaining()) { throw new HPackException(UNEXPECTED_EOS); } final int pos = src.position(); final int b = src.get() & 0xff; src.position(pos); return b; } static int decodeInt(final ByteBuffer src, final int n) throws HPackException { final int nbits = 0xff >>> (8 - n); int value = readByte(src) & nbits; if (value < nbits) { return value; } int m = 0; while (m < 32) { final int b = readByte(src); if ((b & 0x80) != 0) { value += (b & 0x7f) << m; m += 7; } else { if (m == 28 && (b & 0xf8) != 0) { break; } value += b << m; return value; } } throw new HPackException(MAX_LIMIT_EXCEEDED); } static void decodePlainString(final ByteArrayBuffer buffer, final ByteBuffer src) throws HPackException { final int strLen = decodeInt(src, 7); final int remaining = src.remaining(); if (strLen > remaining) { throw new HPackException(UNEXPECTED_EOS); } final int originalLimit = src.limit(); src.limit(originalLimit - (remaining - strLen)); buffer.append(src); src.limit(originalLimit); } static void decodeHuffman(final ByteArrayBuffer buffer, final ByteBuffer src) throws HPackException { final int strLen = decodeInt(src, 7); if (strLen > src.remaining()) { throw new HPackException(UNEXPECTED_EOS); } final int limit = src.limit(); src.limit(src.position() + strLen); Huffman.DECODER.decode(buffer, src); src.limit(limit); } void decodeString(final ByteArrayBuffer buffer, final ByteBuffer src) throws HPackException { final int firstByte = peekByte(src); if ((firstByte & 0x80) == 0x80) { decodeHuffman(buffer, src); } else { decodePlainString(buffer, src); } } private void clearState() { if (this.tmpBuf != null) { this.tmpBuf.clear(); } if (this.charsetDecoder != null) { this.charsetDecoder.reset(); } this.contentBuf.clear(); } private void expandCapacity(final int capacity) { final CharBuffer previous = this.tmpBuf; this.tmpBuf = CharBuffer.allocate(capacity); previous.flip(); this.tmpBuf.put(previous); } private void ensureCapacity(final int extra) { if (this.tmpBuf == null) { this.tmpBuf = CharBuffer.allocate(Math.max(256, extra)); } final int requiredCapacity = this.tmpBuf.remaining() + extra; if (requiredCapacity > this.tmpBuf.capacity()) { expandCapacity(requiredCapacity); } } int decodeString(final ByteBuffer src, final StringBuilder buf) throws HPackException, CharacterCodingException { clearState(); decodeString(this.contentBuf, src); final int binaryLen = this.contentBuf.length(); if (binaryLen == 0) { return 0; } if (this.charsetDecoder == null) { buf.ensureCapacity(binaryLen); for (int i = 0; i < binaryLen; i++) { buf.append((char) (this.contentBuf.byteAt(i) & 0xff)); } } else { final ByteBuffer in = ByteBuffer.wrap(this.contentBuf.array(), 0, binaryLen); while (in.hasRemaining()) { ensureCapacity(in.remaining()); final CoderResult result = this.charsetDecoder.decode(in, this.tmpBuf, true); if (result.isError()) { result.throwException(); } } ensureCapacity(8); final CoderResult result = this.charsetDecoder.flush(this.tmpBuf); if (result.isError()) { result.throwException(); } this.tmpBuf.flip(); buf.append(this.tmpBuf); } return binaryLen; } HPackHeader decodeLiteralHeader( final ByteBuffer src, final HPackRepresentation representation) throws HPackException, CharacterCodingException { final int n = representation == HPackRepresentation.WITH_INDEXING ? 6 : 4; final int index = decodeInt(src, n); final String name; final int nameLen; if (index == 0) { final StringBuilder buf = new StringBuilder(); nameLen = decodeString(src, buf); name = buf.toString(); } else { final HPackHeader existing = this.dynamicTable.getHeader(index); if (existing == null) { throw new HPackException("Invalid header index"); } name = existing.getName(); nameLen = existing.getNameLen(); } final StringBuilder buf = new StringBuilder(); final int valueLen = decodeString(src, buf); final String value = buf.toString(); final HPackHeader header = new HPackHeader(name, nameLen, value, valueLen, representation == HPackRepresentation.NEVER_INDEXED); if (representation == HPackRepresentation.WITH_INDEXING) { this.dynamicTable.add(header); } return header; } HPackHeader decodeIndexedHeader(final ByteBuffer src) throws HPackException { final int index = decodeInt(src, 7); final HPackHeader existing = this.dynamicTable.getHeader(index); if (existing == null) { throw new HPackException("Invalid header index"); } return existing; } public Header decodeHeader(final ByteBuffer src) throws HPackException { final HPackHeader header = decodeHPackHeader(src); return header != null ? new BasicHeader(header.getName(), header.getValue(), header.isSensitive()) : null; } HPackHeader decodeHPackHeader(final ByteBuffer src) throws HPackException { try { while (src.hasRemaining()) { final int b = peekByte(src); if ((b & 0x80) == 0x80) { return decodeIndexedHeader(src); } else if ((b & 0xc0) == 0x40) { return decodeLiteralHeader(src, HPackRepresentation.WITH_INDEXING); } else if ((b & 0xf0) == 0x00) { return decodeLiteralHeader(src, HPackRepresentation.WITHOUT_INDEXING); } else if ((b & 0xf0) == 0x10) { return decodeLiteralHeader(src, HPackRepresentation.NEVER_INDEXED); } else if ((b & 0xe0) == 0x20) { final int maxSize = decodeInt(src, 5); this.dynamicTable.setMaxSize(Math.min(this.maxTableSize, maxSize)); } else { throw new HPackException("Unexpected header first byte: 0x" + Integer.toHexString(b)); } } return null; } catch (final CharacterCodingException ex) { throw new HPackException(ex.getMessage(), ex); } } public List
decodeHeaders(final ByteBuffer src) throws HPackException { final boolean enforceSizeLimit = maxListSize < Integer.MAX_VALUE; int listSize = 0; final List
list = new ArrayList<>(); while (src.hasRemaining()) { final HPackHeader header = decodeHPackHeader(src); if (header == null) { break; } if (enforceSizeLimit) { listSize += header.getTotalSize(); if (listSize >= maxListSize) { throw new HeaderListConstraintException("Maximum header list size exceeded"); } } list.add(new BasicHeader(header.getName(), header.getValue(), header.isSensitive())); } return list; } public int getMaxTableSize() { return this.maxTableSize; } public void setMaxTableSize(final int maxTableSize) { Args.notNegative(maxTableSize, "Max table size"); this.maxTableSize = maxTableSize; this.dynamicTable.setMaxSize(maxTableSize); } public int getMaxListSize() { return maxListSize; } public void setMaxListSize(final int maxListSize) { Args.notNegative(maxListSize, "Max list size"); this.maxListSize = maxListSize; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/InboundDynamicTable.java0100664 0000000 0000000 00000006712 14403631147 026764 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; final class InboundDynamicTable { private final StaticTable staticTable; private final FifoBuffer headers; private int maxSize; private int currentSize; InboundDynamicTable(final StaticTable staticTable) { this.staticTable = staticTable; this.headers = new FifoBuffer(256); this.maxSize = Integer.MAX_VALUE; this.currentSize = 0; } InboundDynamicTable() { this(StaticTable.INSTANCE); } public int getMaxSize() { return maxSize; } public void setMaxSize(final int maxSize) { this.maxSize = maxSize; evict(); } public int getCurrentSize() { return currentSize; } int staticLength() { return staticTable.length(); } int dynamicLength() { return headers.size(); } Header getDynamicEntry(final int index) { return headers.get(index); } public int length() { return staticTable.length() + headers.size(); } public HPackHeader getHeader(final int index) { final int length = length(); Args.check(index >= 1, "index %s cannot be less than 1", index); Args.check(index <= length, "length %s cannot be greater than index %s", length, index); return index <= staticTable.length() ? staticTable.get(index) : headers.get(index - staticTable.length() - 1); } public void add(final HPackHeader header) { final int entrySize = header.getTotalSize(); if (entrySize > this.maxSize) { clear(); return; } headers.addFirst(header); currentSize += entrySize; evict(); } private void clear() { currentSize = 0; headers.clear(); } private void evict() { while (currentSize > maxSize) { final HPackHeader header = headers.removeLast(); if (header != null) { currentSize -= header.getTotalSize(); } else { Asserts.check(currentSize == 0, "Current table size must be zero"); break; } } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HeaderListConstraintException.java0100664 0000000 0000000 00000003401 14403631147 031051 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; /** * Signals a header list constraint violation. * * @since 5.0 */ public class HeaderListConstraintException extends HPackException { /** * Required for serialization support. * * @see java.io.Serializable */ private static final long serialVersionUID = 9130981983188889920L; /** * Creates a HeaderListConstraintException with the specified detail message. * * @param message The exception detail message */ public HeaderListConstraintException(final String message) { super(message); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackEncoder.java0100664 0000000 0000000 00000031030 14403631147 025366 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Objects; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; /** * HPACK encoder. * * @since 5.0 */ @Internal public final class HPackEncoder { private final OutboundDynamicTable dynamicTable; private final ByteArrayBuffer huffmanBuf; private final CharsetEncoder charsetEncoder; private ByteBuffer tmpBuf; private int maxTableSize; HPackEncoder(final OutboundDynamicTable dynamicTable, final CharsetEncoder charsetEncoder) { this.dynamicTable = dynamicTable != null ? dynamicTable : new OutboundDynamicTable(); this.huffmanBuf = new ByteArrayBuffer(128); this.charsetEncoder = charsetEncoder; } HPackEncoder(final OutboundDynamicTable dynamicTable, final Charset charset) { this(dynamicTable, charset != null && !StandardCharsets.US_ASCII.equals(charset) ? charset.newEncoder() : null); } public HPackEncoder(final Charset charset) { this(new OutboundDynamicTable(), charset); } public HPackEncoder(final CharsetEncoder charsetEncoder) { this(new OutboundDynamicTable(), charsetEncoder); } static void encodeInt(final ByteArrayBuffer dst, final int n, final int i, final int mask) { final int nbits = 0xFF >>> (8 - n); int value = i; if (value < nbits) { dst.append(i | mask); } else { dst.append(nbits | mask); value -= nbits; while (value >= 0x80) { dst.append((value & 0x7F) | 0x80); value >>>= 7; } dst.append(value); } } static void encodeHuffman(final ByteArrayBuffer dst, final ByteBuffer src) { Huffman.ENCODER.encode(dst, src); } void encodeString(final ByteArrayBuffer dst, final ByteBuffer src, final boolean huffman) { final int strLen = src.remaining(); if (huffman) { this.huffmanBuf.clear(); this.huffmanBuf.ensureCapacity(strLen); Huffman.ENCODER.encode(this.huffmanBuf, src); dst.ensureCapacity(this.huffmanBuf.length() + 8); encodeInt(dst, 7, this.huffmanBuf.length(), 0x80); dst.append(this.huffmanBuf.array(), 0, this.huffmanBuf.length()); } else { dst.ensureCapacity(strLen + 8); encodeInt(dst, 7, strLen, 0x0); dst.append(src); } } private void clearState() { if (this.tmpBuf != null) { this.tmpBuf.clear(); } if (this.charsetEncoder != null) { this.charsetEncoder.reset(); } } private void expandCapacity(final int capacity) { final ByteBuffer previous = this.tmpBuf; this.tmpBuf = ByteBuffer.allocate(capacity); previous.flip(); this.tmpBuf.put(previous); } private void ensureCapacity(final int extra) { if (this.tmpBuf == null) { this.tmpBuf = ByteBuffer.allocate(Math.max(256, extra)); } final int requiredCapacity = this.tmpBuf.remaining() + extra; if (requiredCapacity > this.tmpBuf.capacity()) { expandCapacity(requiredCapacity); } } int encodeString( final ByteArrayBuffer dst, final CharSequence charSequence, final int off, final int len, final boolean huffman) throws CharacterCodingException { clearState(); if (this.charsetEncoder == null) { if (huffman) { this.huffmanBuf.clear(); this.huffmanBuf.ensureCapacity(len); Huffman.ENCODER.encode(this.huffmanBuf, charSequence, off, len); dst.ensureCapacity(this.huffmanBuf.length() + 8); encodeInt(dst, 7, this.huffmanBuf.length(), 0x80); dst.append(this.huffmanBuf.array(), 0, this.huffmanBuf.length()); } else { dst.ensureCapacity(len + 8); encodeInt(dst, 7, len, 0x0); for (int i = 0; i < len; i++) { dst.append(charSequence.charAt(off + i)); } } return len; } if (charSequence.length() > 0) { final CharBuffer in = CharBuffer.wrap(charSequence, off, len); while (in.hasRemaining()) { ensureCapacity((int) (in.remaining() * this.charsetEncoder.averageBytesPerChar()) + 8); final CoderResult result = this.charsetEncoder.encode(in, this.tmpBuf, true); if (result.isError()) { result.throwException(); } } ensureCapacity(8); final CoderResult result = this.charsetEncoder.flush(this.tmpBuf); if (result.isError()) { result.throwException(); } } this.tmpBuf.flip(); final int binaryLen = this.tmpBuf.remaining(); encodeString(dst, this.tmpBuf, huffman); return binaryLen; } int encodeString(final ByteArrayBuffer dst, final String s, final boolean huffman) throws CharacterCodingException { return encodeString(dst, s, 0, s.length(), huffman); } void encodeLiteralHeader( final ByteArrayBuffer dst, final HPackEntry existing, final Header header, final HPackRepresentation representation, final boolean useHuffman) throws CharacterCodingException { encodeLiteralHeader(dst, existing, header.getName(), header.getValue(), header.isSensitive(), representation, useHuffman); } void encodeLiteralHeader( final ByteArrayBuffer dst, final HPackEntry existing, final String key, final String value, final boolean sensitive, final HPackRepresentation representation, final boolean useHuffman) throws CharacterCodingException { final int n; final int mask; switch (representation) { case WITH_INDEXING: mask = 0x40; n = 6; break; case WITHOUT_INDEXING: mask = 0x00; n = 4; break; case NEVER_INDEXED: mask = 0x10; n = 4; break; default: throw new IllegalStateException("Unexpected value: " + representation); } final int index = existing != null ? existing.getIndex() : 0; final int nameLen; if (index <= 0) { encodeInt(dst, n, 0, mask); nameLen = encodeString(dst, key, useHuffman); } else { encodeInt(dst, n, index, mask); nameLen = existing.getHeader().getNameLen(); } final int valueLen = encodeString(dst, value != null ? value : "", useHuffman); if (representation == HPackRepresentation.WITH_INDEXING) { dynamicTable.add(new HPackHeader(key, nameLen, value, valueLen, sensitive)); } } void encodeIndex(final ByteArrayBuffer dst, final int index) { encodeInt(dst, 7, index, 0x80); } private int findFullMatch(final List entries, final String value) { if (entries == null || entries.isEmpty()) { return 0; } for (int i = 0; i < entries.size(); i++) { final HPackEntry entry = entries.get(i); if (Objects.equals(value, entry.getHeader().getValue())) { return entry.getIndex(); } } return 0; } void encodeHeader( final ByteArrayBuffer dst, final Header header, final boolean noIndexing, final boolean useHuffman) throws CharacterCodingException { encodeHeader(dst, header.getName(), header.getValue(), header.isSensitive(), noIndexing, useHuffman); } void encodeHeader( final ByteArrayBuffer dst, final String name, final String value, final boolean sensitive, final boolean noIndexing, final boolean useHuffman) throws CharacterCodingException { final HPackRepresentation representation; if (sensitive) { representation = HPackRepresentation.NEVER_INDEXED; } else if (noIndexing) { representation = HPackRepresentation.WITHOUT_INDEXING; } else { representation = HPackRepresentation.WITH_INDEXING; } final List staticEntries = StaticTable.INSTANCE.getByName(name); if (representation == HPackRepresentation.WITH_INDEXING) { // Try to find full match and encode as as index final int staticIndex = findFullMatch(staticEntries, value); if (staticIndex > 0) { encodeIndex(dst, staticIndex); return; } final List dynamicEntries = dynamicTable.getByName(name); final int dynamicIndex = findFullMatch(dynamicEntries, value); if (dynamicIndex > 0) { encodeIndex(dst, dynamicIndex); return; } } // Encode as literal HPackEntry existing = null; if (staticEntries != null && !staticEntries.isEmpty()) { existing = staticEntries.get(0); } else { final List dynamicEntries = dynamicTable.getByName(name); if (dynamicEntries != null && !dynamicEntries.isEmpty()) { existing = dynamicEntries.get(0); } } encodeLiteralHeader(dst, existing, name, value, sensitive, representation, useHuffman); } void encodeHeaders( final ByteArrayBuffer dst, final List headers, final boolean noIndexing, final boolean useHuffman) throws CharacterCodingException { for (int i = 0; i < headers.size(); i++) { encodeHeader(dst, headers.get(i), noIndexing, useHuffman); } } public void encodeHeader( final ByteArrayBuffer dst, final Header header) throws CharacterCodingException { Args.notNull(dst, "ByteArrayBuffer"); Args.notNull(header, "Header"); encodeHeader(dst, header.getName(), header.getValue(), header.isSensitive()); } public void encodeHeader( final ByteArrayBuffer dst, final String name, final String value, final boolean sensitive) throws CharacterCodingException { Args.notNull(dst, "ByteArrayBuffer"); Args.notEmpty(name, "Header name"); encodeHeader(dst, name, value, sensitive, false, true); } public void encodeHeaders( final ByteArrayBuffer dst, final List headers, final boolean useHuffman) throws CharacterCodingException { Args.notNull(dst, "ByteArrayBuffer"); Args.notEmpty(headers, "Header list"); encodeHeaders(dst, headers, false, useHuffman); } public int getMaxTableSize() { return this.maxTableSize; } public void setMaxTableSize(final int maxTableSize) { Args.notNegative(maxTableSize, "Max table size"); this.maxTableSize = maxTableSize; this.dynamicTable.setMaxSize(maxTableSize); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackHeader.java0100664 0000000 0000000 00000005715 14245617503 025216 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import org.apache.hc.core5.http.Header; /** * Internal HPack header representation that also contains binary length of * header name and header value. */ final class HPackHeader implements Header { static private final int ENTRY_SIZE_OVERHEAD = 32; private final String name; private final int nameLen; private final String value; private final int valueLen; private final boolean sensitive; HPackHeader(final String name, final int nameLen, final String value, final int valueLen, final boolean sensitive) { this.name = name; this.nameLen = nameLen; this.value = value; this.valueLen = valueLen; this.sensitive = sensitive; } HPackHeader(final String name, final String value, final boolean sensitive) { this(name, name.length(), value, value.length(), sensitive); } HPackHeader(final String name, final String value) { this(name, value, false); } HPackHeader(final Header header) { this(header.getName(), header.getValue(), header.isSensitive()); } @Override public String getName() { return name; } public int getNameLen() { return nameLen; } @Override public String getValue() { return value; } public int getValueLen() { return valueLen; } @Override public boolean isSensitive() { return sensitive; } public int getTotalSize() { return nameLen + valueLen + ENTRY_SIZE_OVERHEAD; } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append(this.name).append(": "); if (this.value != null) { buf.append(this.value); } return buf.toString(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/FifoLinkedList.java0100664 0000000 0000000 00000010433 14403631147 025752 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.util.Args; final class FifoLinkedList { private final InternalNode master; private int length; FifoLinkedList() { this.master = new InternalNode(null); this.master.previous = this.master; this.master.next = this.master; } public Header get(final int index) { Args.check(index <= length, "Length %s cannot be greater then index %s ", length, index); Args.notNegative(index, "index"); InternalNode current = master.next; int n = 0; while (current != master) { if (index == n) { return current.header; } current = current.next; n++; } return null; } public int getIndex(final InternalNode node) { final int seqNum = node.seqNum; if (seqNum < 1) { return -1; } return length - (seqNum - master.previous.seqNum) - 1; } public Header getFirst() { return master.next.header; } public Header getLast() { return master.previous.header; } public int size() { return length; } public InternalNode addFirst(final HPackHeader header) { final InternalNode newNode = new InternalNode(header); final InternalNode oldNode = master.next; master.next = newNode; newNode.previous = master; newNode.next = oldNode; oldNode.previous = newNode; newNode.seqNum = oldNode.seqNum + 1; length++; return newNode; } public InternalNode removeLast() { final InternalNode last = master.previous; if (last.header != null) { final InternalNode lastButOne = last.previous; master.previous = lastButOne; lastButOne.next = master; last.previous = null; last.next = null; last.seqNum = 0; length--; return last; } master.seqNum = 0; return null; } public void clear() { master.previous = master; master.next = master; master.seqNum = 0; length = 0; } class InternalNode implements HPackEntry { private final HPackHeader header; private InternalNode previous; private InternalNode next; private int seqNum; InternalNode(final HPackHeader header) { this.header = header; } @Override public HPackHeader getHeader() { return header; } @Override public int getIndex() { return StaticTable.INSTANCE.length() + FifoLinkedList.this.getIndex(this) + 1; } @Override public String toString() { return "[" + (header != null ? header.toString() : "master") + "; seqNum=" + seqNum + "; previous=" + (previous != null ? previous.header : null) + "; next=" + (next != null ? next.header : null) + ']'; } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackEntry.java0100664 0000000 0000000 00000002430 14245617503 025116 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; interface HPackEntry { int getIndex(); HPackHeader getHeader(); } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/FifoBuffer.java0100664 0000000 0000000 00000006136 14245617503 025132 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import org.apache.hc.core5.http.Header; final class FifoBuffer { private HPackHeader[] array; private int head; private int tail; FifoBuffer(final int initialCapacity) { this.array = new HPackHeader[initialCapacity]; this.head = 0; this.tail = 0; } private void expand() { int newcapacity = (array.length + 1) << 1; if (newcapacity < 0) { newcapacity = Integer.MAX_VALUE; } final Header[] oldArray = array; final int len = oldArray.length; final HPackHeader[] newArray = new HPackHeader[newcapacity]; System.arraycopy(oldArray, head, newArray, 0, len - head); System.arraycopy(oldArray, 0, newArray, len - head, head); array = newArray; head = len; tail = 0; } public void clear() { head = 0; tail = 0; } public void addFirst(final HPackHeader header) { array[head++] = header; if (head == array.length) { head = 0; } if (head == tail) { expand(); } } public HPackHeader get(final int index) { int i = head - index - 1; if (i < 0) { i = array.length + i; } return array[i]; } public HPackHeader getFirst() { return array[head > 0 ? head - 1 : array.length - 1]; } public HPackHeader getLast() { return array[tail]; } public HPackHeader removeLast() { final HPackHeader header = array[tail]; if (header != null) { array[tail++] = null; if (tail == array.length) { tail = 0; } } return header; } public int capacity() { return array.length; } public int size() { int i = head - tail; if (i < 0) { i = array.length + i; } return i; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackException.java0100664 0000000 0000000 00000003145 14245617503 025757 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import org.apache.hc.core5.http.HttpException; /** * Signals HPACK protocol violation. * * @since 5.0 */ public class HPackException extends HttpException { private static final long serialVersionUID = 1L; public HPackException(final String message) { super(message); } public HPackException(final String message, final Exception cause) { super(message, cause); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/StaticTable.java0100664 0000000 0000000 00000014436 14245617503 025316 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.hc.core5.http2.H2PseudoRequestHeaders; import org.apache.hc.core5.http2.H2PseudoResponseHeaders; final class StaticTable { static final HPackHeader[] STANDARD_HEADERS = { new HPackHeader(H2PseudoRequestHeaders.AUTHORITY, ""), new HPackHeader(H2PseudoRequestHeaders.METHOD, "GET"), new HPackHeader(H2PseudoRequestHeaders.METHOD, "POST"), new HPackHeader(H2PseudoRequestHeaders.PATH, "/"), new HPackHeader(H2PseudoRequestHeaders.PATH, "/index.html"), new HPackHeader(H2PseudoRequestHeaders.SCHEME, "http"), new HPackHeader(H2PseudoRequestHeaders.SCHEME, "https"), new HPackHeader(H2PseudoResponseHeaders.STATUS, "200"), new HPackHeader(H2PseudoResponseHeaders.STATUS, "204"), new HPackHeader(H2PseudoResponseHeaders.STATUS, "206"), new HPackHeader(H2PseudoResponseHeaders.STATUS, "304"), new HPackHeader(H2PseudoResponseHeaders.STATUS, "400"), new HPackHeader(H2PseudoResponseHeaders.STATUS, "404"), new HPackHeader(H2PseudoResponseHeaders.STATUS, "500"), new HPackHeader("accept-charset", ""), new HPackHeader("accept-encoding", "gzip, deflate"), new HPackHeader("accept-language", ""), new HPackHeader("accept-ranges", ""), new HPackHeader("accept", ""), new HPackHeader("access-control-allow-origin", ""), new HPackHeader("age", ""), new HPackHeader("allow", ""), new HPackHeader("authorization", ""), new HPackHeader("cache-control", ""), new HPackHeader("content-disposition", ""), new HPackHeader("content-encoding", ""), new HPackHeader("content-language", ""), new HPackHeader("content-length", ""), new HPackHeader("content-location", ""), new HPackHeader("content-range", ""), new HPackHeader("content-type", ""), new HPackHeader("cookie", ""), new HPackHeader("date", ""), new HPackHeader("etag", ""), new HPackHeader("expect", ""), new HPackHeader("expires", ""), new HPackHeader("from", ""), new HPackHeader("host", ""), new HPackHeader("if-match", ""), new HPackHeader("if-modified-since", ""), new HPackHeader("if-none-match", ""), new HPackHeader("if-range", ""), new HPackHeader("if-unmodified-since", ""), new HPackHeader("last-modified", ""), new HPackHeader("link", ""), new HPackHeader("location", ""), new HPackHeader("max-forwards", ""), new HPackHeader("proxy-authenticate", ""), new HPackHeader("proxy-authorization", ""), new HPackHeader("range", ""), new HPackHeader("referer", ""), new HPackHeader("refresh", ""), new HPackHeader("retry-after", ""), new HPackHeader("server", ""), new HPackHeader("set-cookie", ""), new HPackHeader("strict-transport-security", ""), new HPackHeader("transfer-encoding", ""), new HPackHeader("user-agent", ""), new HPackHeader("vary", ""), new HPackHeader("via", ""), new HPackHeader("www-authenticate", "") }; final static StaticTable INSTANCE = new StaticTable(STANDARD_HEADERS); private final HPackHeader[] headers; private final ConcurrentMap> mapByName; StaticTable(final HPackHeader... headers) { this.headers = headers; this.mapByName = new ConcurrentHashMap<>(); for (int i = 0; i < headers.length; i++) { final HPackHeader header = headers[i]; final String key = header.getName(); CopyOnWriteArrayList entries = this.mapByName.get(key); if (entries == null) { entries = new CopyOnWriteArrayList<>(new HPackEntry[] { new InternalEntry(header, i) }); this.mapByName.put(key, entries); } else { entries.add(new InternalEntry(header, i)); } } } public int length() { return this.headers.length; } public HPackHeader get(final int index) { return this.headers[index - 1]; } public List getByName(final String key) { return this.mapByName.get(key); } static class InternalEntry implements HPackEntry { private final HPackHeader header; private final int index; InternalEntry(final HPackHeader header, final int index) { this.header = header; this.index = index; } @Override public int getIndex() { return index + 1; } @Override public HPackHeader getHeader() { return header; } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HuffmanDecoder.java0100664 0000000 0000000 00000010326 14245617503 025763 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import java.nio.ByteBuffer; import org.apache.hc.core5.util.ByteArrayBuffer; /** * This Huffman codec implementation has been derived from Twitter HPack project * (https://github.com/twitter/hpack) */ final class HuffmanDecoder { private final HuffmanNode root; HuffmanDecoder(final int[] codes, final byte[] lengths) { root = buildTree(codes, lengths); } void decode(final ByteArrayBuffer out, final ByteBuffer src) throws HPackException { HuffmanNode node = this.root; int current = 0; int bits = 0; while (src.hasRemaining()) { final int b = src.get() & 0xFF; current = (current << 8) | b; bits += 8; while (bits >= 8) { final int c = (current >>> (bits - 8)) & 0xFF; node = node.getChild(c); bits -= node.getBits(); if (node.isTerminal()) { if (node.getSymbol() == Huffman.EOS) { throw new HPackException("EOS decoded"); } out.append(node.getSymbol()); node = root; } } } while (bits > 0) { final int c = (current << (8 - bits)) & 0xFF; node = node.getChild(c); if (node.isTerminal() && node.getBits() <= bits) { bits -= node.getBits(); out.append(node.getSymbol()); node = this.root; } else { break; } } // Section 5.2. String Literal Representation // Padding not corresponding to the most significant bits of the code // for the EOS symbol (0xFF) MUST be treated as a decoding error. final int mask = (1 << bits) - 1; if ((current & mask) != mask) { throw new HPackException("Invalid padding"); } } private static HuffmanNode buildTree(final int[] codes, final byte[] lengths) { final HuffmanNode root = new HuffmanNode(); for (int symbol = 0; symbol < codes.length; symbol++) { final int code = codes[symbol]; int length = lengths[symbol]; HuffmanNode current = root; while (length > 8) { if (current.isTerminal()) { throw new IllegalStateException("Invalid Huffman code: prefix not unique"); } length -= 8; final int i = (code >>> length) & 0xFF; if (!current.hasChild(i)) { current.setChild(i, new HuffmanNode()); } current = current.getChild(i); } final HuffmanNode terminal = new HuffmanNode(symbol, length); final int shift = 8 - length; final int start = (code << shift) & 0xFF; final int end = 1 << shift; for (int i = start; i < start + end; i++) { current.setChild(i, terminal); } } return root; } }httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackRepresentation.java0100664 0000000 0000000 00000002426 14245617503 027024 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; enum HPackRepresentation { WITH_INDEXING, WITHOUT_INDEXING, NEVER_INDEXED }httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/Huffman.java0100664 0000000 0000000 00000020206 14245617503 024473 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import org.apache.hc.core5.annotation.Internal; /** * This Huffman codec implementation has been derived from Twitter HPack project * (https://github.com/twitter/hpack) */ @Internal public final class Huffman { private Huffman() { // Do not allow utility class to be instantiated. } static final int[] CODES = { 0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, 0xfffffe7, 0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, 0x3ffffffd, 0xfffffeb, 0xfffffec, 0xfffffed, 0xfffffee, 0xfffffef, 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3, 0xffffff4, 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, 0xffffffb, 0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa, 0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18, 0x0, 0x1, 0x2, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, 0xffb, 0x3fc, 0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, 0x1ffc, 0x3ffc, 0x22, 0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26, 0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7, 0x2b, 0x76, 0x2c, 0x8, 0x9, 0x2d, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, 0x1ffd, 0xffffffc, 0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, 0x3fffd4, 0x3fffd5, 0x7fffd9, 0x3fffd6, 0x7fffda, 0x7fffdb, 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf, 0xffffec, 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, 0x7fffe3, 0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, 0x7fffe6, 0x7fffe7, 0xffffef, 0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde, 0x7fffea, 0x3fffdd, 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec, 0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, 0x7fffee, 0x7fffef, 0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, 0x3fffe5, 0x3fffe6, 0x7ffff1, 0x3ffffe0, 0x3ffffe1, 0xfffeb, 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec, 0x3ffffe2, 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, 0x1ffffed, 0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, 0x3ffffe7, 0x7ffffe2, 0xfffff2, 0x1fffe4, 0x1fffe5, 0x3ffffe8, 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5, 0xfffec, 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3, 0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, 0x3ffffea, 0x7ffff4, 0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea, 0x7ffffeb, 0xffffffe, 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee, 0x3fffffff }; static final byte[] LENGTHS = { 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, 6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10, 13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6, 15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5, 6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28, 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23, 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24, 22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23, 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23, 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25, 19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27, 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23, 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26, 30 }; static final int EOS = 256; static final HuffmanEncoder ENCODER = new HuffmanEncoder(CODES, LENGTHS); static final HuffmanDecoder DECODER = new HuffmanDecoder(CODES, LENGTHS); } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HuffmanEncoder.java0100664 0000000 0000000 00000005733 14245617503 026003 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import java.nio.ByteBuffer; import org.apache.hc.core5.util.ByteArrayBuffer; /** * This Huffman codec implementation has been derived from Twitter HPack project * (https://github.com/twitter/hpack) */ final class HuffmanEncoder { private final int[] codes; private final byte[] lengths; HuffmanEncoder(final int[] codes, final byte[] lengths) { this.codes = codes; this.lengths = lengths; } void encode(final ByteArrayBuffer out, final ByteBuffer src) { long current = 0; int n = 0; while (src.hasRemaining()) { final int b = src.get() & 0xFF; final int code = codes[b]; final int nbits = lengths[b]; current <<= nbits; current |= code; n += nbits; while (n >= 8) { n -= 8; out.append((int)(current >> n)); } } if (n > 0) { current <<= (8 - n); current |= (0xFF >>> n); // this should be EOS symbol out.append((int) current); } } void encode(final ByteArrayBuffer out, final CharSequence src, final int off, final int len) { long current = 0; int n = 0; for (int i = 0; i < len; i++) { final int b = src.charAt(off + i) & 0xFF; final int code = codes[b]; final int nbits = lengths[b]; current <<= nbits; current |= code; n += nbits; while (n >= 8) { n -= 8; out.append((int)(current >> n)); } } if (n > 0) { current <<= (8 - n); current |= (0xFF >>> n); // this should be EOS symbol out.append((int) current); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HuffmanNode.java0100664 0000000 0000000 00000005145 14245617503 025306 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import java.util.Arrays; import org.apache.hc.core5.util.Asserts; /** * This Huffman codec implementation has been derived from Twitter HPack project * (https://github.com/twitter/hpack) */ final class HuffmanNode { private final int symbol; private final int bits; private final HuffmanNode[] children; HuffmanNode() { this.symbol = 0; this.bits = 8; this.children = new HuffmanNode[256]; } HuffmanNode(final int symbol, final int bits) { this.symbol = symbol; this.bits = bits; this.children = null; } public int getBits() { return this.bits; } public int getSymbol() { return this.symbol; } public boolean hasChild(final int index) { return this.children != null && this.children[index] != null; } public HuffmanNode getChild(final int index) { return this.children != null ? this.children[index] : null; } void setChild(final int index, final HuffmanNode child) { Asserts.notNull(this.children, "Children nodes"); this.children[index] = child; } public boolean isTerminal() { return this.children == null; } @Override public String toString() { return "[" + "symbol=" + symbol + ", bits=" + bits + ", children=" + Arrays.toString(children) + ']'; } }httpcore5-h2/src/main/java/org/apache/hc/core5/http2/H2Error.java0100664 0000000 0000000 00000010360 14245617503 023304 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Errors codes defined by HTTP/2 specification. * * @since 5.0 */ public enum H2Error { /** * Graceful shutdown *

* The associated condition is not as a result of an error. */ NO_ERROR (0x00), /** * Protocol error detected *

* The endpoint detected an unspecific protocol error. This error is for use when a more * specific error code is not available */ PROTOCOL_ERROR (0x01), /** * Implementation fault *

* The endpoint encountered an unexpected internal error. */ INTERNAL_ERROR (0x02), /** * Flow control limits exceeded. *

* The endpoint detected that its peer violated the flow control protocol. */ FLOW_CONTROL_ERROR (0x03), /** * Settings not acknowledged. *

* The endpoint sent a SETTINGS frame, but did not receive a response in a timely manner. */ SETTINGS_TIMEOUT (0x04), /** * Frame received for closed stream. *

* The endpoint received a frame after a stream was half closed. */ STREAM_CLOSED (0x05), /** * Frame size incorrect. *

* The endpoint received a frame with an invalid size. */ FRAME_SIZE_ERROR (0x06), /** * Stream not processed *

* The endpoint refuses the stream prior to performing any application processing. */ REFUSED_STREAM (0x07), /** * Stream canceled. *

* Used by the endpoint to indicate that the stream is no longer needed */ CANCEL (0x08), /** * Compression state not updated. *

* The endpoint is unable to maintain the header compression context for the connection. */ COMPRESSION_ERROR (0x09), /** * TCP connection error. *

* The connection established in response to a CONNECT request was reset or abnormally closed. */ CONNECT_ERROR (0x0a), /** * Processing capacity exceeded. *

* The endpoint detected that its peer is exhibiting a behavior that might be generating * excessive load. */ ENHANCE_YOUR_CALM (0x0b), /** * Negotiated TLS parameters not acceptable. *

* The underlying transport has properties that do not meet minimum security requirements. */ INADEQUATE_SECURITY (0x0c), /** * Use HTTP/1.1 for the request. *

* The endpoint requires that HTTP/1.1 be used instead of HTTP/2. */ HTTP_1_1_REQUIRED (0x0d); int code; H2Error(final int code) { this.code = code; } public int getCode() { return code; } private static final ConcurrentMap MAP_BY_CODE; static { MAP_BY_CODE = new ConcurrentHashMap<>(); for (final H2Error error: values()) { MAP_BY_CODE.putIfAbsent(error.code, error); } } public static H2Error getByCode(final int code) { return MAP_BY_CODE.get(code); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/H2PseudoRequestHeaders.java0100664 0000000 0000000 00000003053 14245617503 026320 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2; /** * Request pseudo HTTP headers defined by the HTTP/2 specification. * * @since 5.0 */ public final class H2PseudoRequestHeaders { public static final String METHOD = ":method"; public static final String SCHEME = ":scheme"; public static final String AUTHORITY = ":authority"; public static final String PATH = ":path"; } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/protocol/0040775 0000000 0000000 00000000000 14403631147 023016 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/protocol/package-info.java0100664 0000000 0000000 00000002366 14245617503 026215 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP/2 protocol interceptors. */ package org.apache.hc.core5.http2.protocol; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/protocol/H2ResponseContent.java0100664 0000000 0000000 00000005523 14403631147 027206 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.ResponseContent; import org.apache.hc.core5.util.Args; /** * HTTP/2 compatible extension of {@link ResponseContent}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class H2ResponseContent extends ResponseContent { /** * Singleton instance. * * @since 5.2 */ public static final H2ResponseContent INSTANCE = new H2ResponseContent(); public H2ResponseContent() { super(); } public H2ResponseContent(final boolean overwrite) { super(overwrite); } @Override public void process( final HttpResponse response, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(context, "HTTP context"); final ProtocolVersion ver = context.getProtocolVersion(); if (ver.getMajor() < 2) { super.process(response, entity, context); } else if (entity != null) { MessageSupport.addContentTypeHeader(response, entity); MessageSupport.addContentEncodingHeader(response, entity); MessageSupport.addTrailerHeader(response, entity); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/protocol/H2RequestConnControl.java0100664 0000000 0000000 00000004651 14403631147 027665 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.RequestConnControl; import org.apache.hc.core5.util.Args; /** * HTTP/2 compatible extension of {@link RequestConnControl}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class H2RequestConnControl extends RequestConnControl { /** * Singleton instance. * * @since 5.2 */ public static final H2RequestConnControl INSTANCE = new H2RequestConnControl(); @Override public void process( final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(context, "HTTP context"); final ProtocolVersion ver = context.getProtocolVersion(); if (ver.getMajor() < 2) { super.process(request, entity, context); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/protocol/H2RequestValidateHost.java0100664 0000000 0000000 00000004657 14403631147 030024 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.RequestValidateHost; import org.apache.hc.core5.util.Args; /** * HTTP/2 compatible extension of {@link RequestValidateHost}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class H2RequestValidateHost extends RequestValidateHost { /** * Singleton instance. * * @since 5.2 */ public static final H2RequestValidateHost INSTANCE = new H2RequestValidateHost(); @Override public void process( final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(context, "HTTP context"); final ProtocolVersion ver = context.getProtocolVersion(); if (ver.getMajor() < 2) { super.process(request, entity, context); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/protocol/H2ResponseConnControl.java0100664 0000000 0000000 00000004662 14403631147 030035 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.ResponseConnControl; import org.apache.hc.core5.util.Args; /** * HTTP/2 compatible extension of {@link ResponseConnControl}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class H2ResponseConnControl extends ResponseConnControl { /** * Singleton instance. * * @since 5.2 */ public static final H2ResponseConnControl INSTANCE = new H2ResponseConnControl(); @Override public void process( final HttpResponse response, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(context, "HTTP context"); final ProtocolVersion ver = context.getProtocolVersion(); if (ver.getMajor() < 2) { super.process(response, entity, context); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/protocol/H2RequestTargetHost.java0100664 0000000 0000000 00000004643 14403631147 027514 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.RequestTargetHost; import org.apache.hc.core5.util.Args; /** * HTTP/2 compatible extension of {@link RequestTargetHost}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class H2RequestTargetHost extends RequestTargetHost { /** * Singleton instance. * * @since 5.2 */ public static final H2RequestTargetHost INSTANCE = new H2RequestTargetHost(); @Override public void process( final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(context, "HTTP context"); final ProtocolVersion ver = context.getProtocolVersion(); if (ver.getMajor() < 2) { super.process(request, entity, context); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/protocol/H2RequestContent.java0100664 0000000 0000000 00000006152 14403631147 027037 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.protocol; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.RequestContent; import org.apache.hc.core5.util.Args; /** * HTTP/2 compatible extension of {@link RequestContent}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class H2RequestContent extends RequestContent { /** * Singleton instance. * * @since 5.2 */ public static final H2RequestContent INSTANCE = new H2RequestContent(); public H2RequestContent() { super(); } public H2RequestContent(final boolean overwrite) { super(overwrite); } @Override public void process( final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(context, "HTTP context"); final ProtocolVersion ver = context.getProtocolVersion(); if (ver.getMajor() < 2) { super.process(request, entity, context); } else if (entity != null) { final String method = request.getMethod(); if (Method.TRACE.isSame(method)) { throw new ProtocolException("TRACE request may not enclose an entity"); } MessageSupport.addContentTypeHeader(request, entity); MessageSupport.addContentEncodingHeader(request, entity); MessageSupport.addTrailerHeader(request, entity); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/0040775 0000000 0000000 00000000000 14245617503 021746 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/package-info.java0100664 0000000 0000000 00000002450 14245617503 025133 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP/2 transport specific APIs based on the asynchronous, * event driven I/O model. */ package org.apache.hc.core5.http2.nio; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/0040775 0000000 0000000 00000000000 14315123013 023444 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/package-info.java0100664 0000000 0000000 00000002416 14245617503 026651 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Support classes for HTTP/2 asynchronous transport. */ package org.apache.hc.core5.http2.nio.support; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/BasicPingHandler.java0100664 0000000 0000000 00000005036 14315123013 027445 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.nio.support; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http2.nio.AsyncPingHandler; import org.apache.hc.core5.util.Args; /** * Basic {@link AsyncPingHandler} implementation. * * @since 5.0 */ public class BasicPingHandler implements AsyncPingHandler { private static final byte[] PING_MESSAGE = new byte[] {'*', '*', 'p', 'i', 'n', 'g', '*', '*'}; private final Callback callback; public BasicPingHandler(final Callback callback) { this.callback = Args.notNull(callback, "Callback"); } @Override public ByteBuffer getData() { return ByteBuffer.wrap(PING_MESSAGE); } @Override public void consumeResponse(final ByteBuffer feedback) throws HttpException, IOException { boolean result = true; for (int i = 0; i < PING_MESSAGE.length; i++) { if (!feedback.hasRemaining() || PING_MESSAGE[i] != feedback.get()) { result = false; break; } } callback.execute(result); } @Override public void failed(final Exception cause) { callback.execute(Boolean.FALSE); } @Override public void cancel() { callback.execute(Boolean.FALSE); } } ././@LongLink0100644 0000000 0000000 00000000146 14245617503 011643 Lustar 0000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/DefaultAsyncPushConsumerFactory.javahttpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/DefaultAsyncPushConsumerFactory.jav0100664 0000000 0000000 00000005116 14245617503 032452 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.nio.support; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestMapper; import org.apache.hc.core5.http.MisdirectedRequestException; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Factory for {@link AsyncPushConsumer} instances that make use * of {@link HttpRequestMapper} to dispatch * the request to a particular {@link AsyncPushConsumer} for processing. * * @since 5.0 */ public final class DefaultAsyncPushConsumerFactory implements HandlerFactory { private final HttpRequestMapper> mapper; public DefaultAsyncPushConsumerFactory(final HttpRequestMapper> mapper) { this.mapper = Args.notNull(mapper, "Request handler mapper"); } @Override public AsyncPushConsumer create(final HttpRequest request, final HttpContext context) throws HttpException { try { final Supplier supplier = mapper.resolve(request, context); return supplier != null ? supplier.get() : null; } catch (final MisdirectedRequestException ex) { return null; } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AsyncPingHandler.java0100664 0000000 0000000 00000003764 14245617503 026011 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.nio; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.hc.core5.http.HttpException; /** * Abstract asynchronous ping exchange handler. * * @since 5.0 */ public interface AsyncPingHandler { /** * Returns content of ping message. * * @return the ping message content. */ ByteBuffer getData(); /** * Triggered to signal receipt of a ping response message. * * @param feedback the ping message feedback. */ void consumeResponse(ByteBuffer feedback) throws HttpException, IOException; /** * Triggered to signal a failure in data processing. * * @param cause the cause of the failure. */ void failed(Exception cause); /** * Triggered to cancel the message exchange. */ void cancel(); } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/pool/0040775 0000000 0000000 00000000000 14403631147 022713 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/pool/package-info.java0100664 0000000 0000000 00000002375 14245617503 026112 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP/2 specific pool component APIs. */ package org.apache.hc.core5.http2.nio.pool; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/pool/H2ConnPool.java0100664 0000000 0000000 00000015155 14403631147 025503 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.nio.pool; import java.net.InetSocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.DefaultAddressResolver; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http2.nio.command.PingCommand; import org.apache.hc.core5.http2.nio.support.BasicPingHandler; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.AbstractIOSessionPool; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Pool of HTTP/2 message multiplexing capable connections. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public final class H2ConnPool extends AbstractIOSessionPool { private final ConnectionInitiator connectionInitiator; private final Resolver addressResolver; private final TlsStrategy tlsStrategy; private volatile TimeValue validateAfterInactivity = TimeValue.NEG_ONE_MILLISECOND; public H2ConnPool( final ConnectionInitiator connectionInitiator, final Resolver addressResolver, final TlsStrategy tlsStrategy) { super(); this.connectionInitiator = Args.notNull(connectionInitiator, "Connection initiator"); this.addressResolver = addressResolver != null ? addressResolver : DefaultAddressResolver.INSTANCE; this.tlsStrategy = tlsStrategy; } public TimeValue getValidateAfterInactivity() { return validateAfterInactivity; } public void setValidateAfterInactivity(final TimeValue timeValue) { this.validateAfterInactivity = timeValue; } @Override protected void closeSession( final IOSession ioSession, final CloseMode closeMode) { if (closeMode == CloseMode.GRACEFUL) { ioSession.enqueue(ShutdownCommand.GRACEFUL, Command.Priority.NORMAL); } else { ioSession.close(closeMode); } } @Override protected Future connectSession( final HttpHost namedEndpoint, final Timeout connectTimeout, final FutureCallback callback) { final InetSocketAddress remoteAddress = addressResolver.resolve(namedEndpoint); return connectionInitiator.connect( namedEndpoint, remoteAddress, null, connectTimeout, null, new CallbackContribution(callback) { @Override public void completed(final IOSession ioSession) { if (tlsStrategy != null && URIScheme.HTTPS.same(namedEndpoint.getSchemeName()) && ioSession instanceof TransportSecurityLayer) { tlsStrategy.upgrade( (TransportSecurityLayer) ioSession, namedEndpoint, null, connectTimeout, new CallbackContribution(callback) { @Override public void completed(final TransportSecurityLayer transportSecurityLayer) { callback.completed(ioSession); } }); ioSession.setSocketTimeout(connectTimeout); } else { callback.completed(ioSession); } } }); } @Override protected void validateSession( final IOSession ioSession, final Callback callback) { if (ioSession.isOpen()) { final TimeValue timeValue = validateAfterInactivity; if (TimeValue.isNonNegative(timeValue)) { final long lastAccessTime = Math.min(ioSession.getLastReadTime(), ioSession.getLastWriteTime()); final long deadline = lastAccessTime + timeValue.toMilliseconds(); if (deadline <= System.currentTimeMillis()) { final Timeout socketTimeoutMillis = ioSession.getSocketTimeout(); ioSession.enqueue(new PingCommand(new BasicPingHandler(result -> { ioSession.setSocketTimeout(socketTimeoutMillis); callback.execute(result); })), Command.Priority.NORMAL); return; } } callback.execute(true); } else { callback.execute(false); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/command/0040775 0000000 0000000 00000000000 14245617503 023364 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/command/package-info.java0100664 0000000 0000000 00000002473 14245617503 026556 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP/2 specific commands for HTTP/2 transport based * on asynchronous, event driven I/O model. */ package org.apache.hc.core5.http2.nio.command; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/command/PingCommand.java0100664 0000000 0000000 00000003517 14245617503 026426 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.nio.command; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http2.nio.AsyncPingHandler; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.util.Args; /** * HTTP/2 ping command. * * @since 5.0 */ @Internal public final class PingCommand implements Command { private final AsyncPingHandler handler; public PingCommand(final AsyncPingHandler handler) { this.handler = Args.notNull(handler, "Handler"); } public AsyncPingHandler getHandler() { return handler; } @Override public boolean cancel() { handler.cancel(); return true; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/0040775 0000000 0000000 00000000000 14403631147 021756 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/package-info.java0100664 0000000 0000000 00000002371 14245617503 025151 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP/2 specific TLS protocol support. */ package org.apache.hc.core5.http2.ssl; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2TlsSupport.java0100664 0000000 0000000 00000006773 14403631147 025164 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.ssl; import javax.net.ssl.SSLParameters; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.http.ssl.TlsCiphers; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.util.ReflectionUtils; /** * HTTP/2 TLS support methods * * @since 5.0 */ public final class H2TlsSupport { public static void setEnableRetransmissions(final SSLParameters sslParameters, final boolean value) { ReflectionUtils.callSetter(sslParameters, "EnableRetransmissions", Boolean.TYPE, value); } /** * @deprecated Use {@link SSLParameters#setApplicationProtocols(String[])}. */ @Deprecated public static void setApplicationProtocols(final SSLParameters sslParameters, final String[] values) { ReflectionUtils.callSetter(sslParameters, "ApplicationProtocols", String[].class, values); } public static String[] selectApplicationProtocols(final Object attachment) { final HttpVersionPolicy versionPolicy = attachment instanceof HttpVersionPolicy ? (HttpVersionPolicy) attachment : HttpVersionPolicy.NEGOTIATE; switch (versionPolicy) { case FORCE_HTTP_1: return new String[] { ApplicationProtocol.HTTP_1_1.id }; case FORCE_HTTP_2: return new String[] { ApplicationProtocol.HTTP_2.id }; default: return new String[] { ApplicationProtocol.HTTP_2.id, ApplicationProtocol.HTTP_1_1.id }; } } public static SSLSessionInitializer enforceRequirements( final Object attachment, final SSLSessionInitializer initializer) { return (endpoint, sslEngine) -> { final SSLParameters sslParameters = sslEngine.getSSLParameters(); sslParameters.setProtocols(TLS.excludeWeak(sslParameters.getProtocols())); sslParameters.setCipherSuites(TlsCiphers.excludeH2Blacklisted(sslParameters.getCipherSuites())); setEnableRetransmissions(sslParameters, false); sslParameters.setApplicationProtocols(selectApplicationProtocols(attachment)); sslEngine.setSSLParameters(sslParameters); if (initializer != null) { initializer.initialize(endpoint, sslEngine); } }; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ClientTlsStrategy.java0100664 0000000 0000000 00000011562 14403631147 026441 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.ssl; import java.net.SocketAddress; import javax.net.ssl.SSLContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * Basic client-side implementation of {@link TlsStrategy} that upgrades to TLS for all endpoints * with {@code HTTPS} scheme. * * @since 5.0 */ public class H2ClientTlsStrategy implements TlsStrategy { private final SSLContext sslContext; private final SSLBufferMode sslBufferMode; private final SSLSessionInitializer initializer; private final SSLSessionVerifier verifier; public H2ClientTlsStrategy( final SSLContext sslContext, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this.sslContext = Args.notNull(sslContext, "SSL context"); this.sslBufferMode = sslBufferMode; this.initializer = initializer; this.verifier = verifier; } public H2ClientTlsStrategy( final SSLContext sslContext, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this(sslContext, null, initializer, verifier); } public H2ClientTlsStrategy( final SSLContext sslContext, final SSLSessionVerifier verifier) { this(sslContext, null, null, verifier); } public H2ClientTlsStrategy(final SSLContext sslContext) { this(sslContext, null, null, null); } public H2ClientTlsStrategy() { this(SSLContexts.createSystemDefault()); } /** * Constructor with the default SSL context based on system properties and custom {@link SSLSessionVerifier}. * @param verifier the custom {@link SSLSessionVerifier}. * @see SSLContext * @since 5.2 */ public H2ClientTlsStrategy( final SSLSessionVerifier verifier) { this(SSLContexts.createSystemDefault(), null, null, verifier); } @Override public void upgrade( final TransportSecurityLayer tlsSession, final NamedEndpoint endpoint, final Object attachment, final Timeout handshakeTimeout, final FutureCallback callback) { tlsSession.startTls( sslContext, endpoint, sslBufferMode, H2TlsSupport.enforceRequirements(attachment, initializer), verifier, handshakeTimeout, callback); } /** * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)} */ @Deprecated @Override public boolean upgrade( final TransportSecurityLayer tlsSession, final HttpHost host, final SocketAddress localAddress, final SocketAddress remoteAddress, final Object attachment, final Timeout handshakeTimeout) { final String scheme = host != null ? host.getSchemeName() : null; if (URIScheme.HTTPS.same(scheme)) { upgrade(tlsSession, host, attachment, handshakeTimeout, null); return true; } return false; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ConscryptClientTlsStrategy.java0100664 0000000 0000000 00000012072 14403631147 030151 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.ssl; import java.net.SocketAddress; import javax.net.ssl.SSLContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * Basic client-side implementation of {@link TlsStrategy} that upgrades to TLS for all endpoints * with {@code HTTPS} scheme. * * @since 5.0 */ public class ConscryptClientTlsStrategy implements TlsStrategy { private final SSLContext sslContext; private final SSLBufferMode sslBufferMode; private final SSLSessionInitializer initializer; private final SSLSessionVerifier verifier; public ConscryptClientTlsStrategy( final SSLContext sslContext, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this.sslContext = Args.notNull(sslContext, "SSL context"); this.sslBufferMode = sslBufferMode; this.initializer = initializer; this.verifier = verifier; } public ConscryptClientTlsStrategy( final SSLContext sslContext, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this(sslContext, null, initializer, verifier); } public ConscryptClientTlsStrategy( final SSLContext sslContext, final SSLSessionVerifier verifier) { this(sslContext, null, null, verifier); } public ConscryptClientTlsStrategy(final SSLContext sslContext) { this(sslContext, null, null, null); } /** * Empty constructor with the default SSL context based on system properties. * @see SSLContext * @since 5.2 */ public ConscryptClientTlsStrategy() { this(SSLContexts.createSystemDefault(), null, null, null); } /** * Constructor with the default SSL context based on system properties and custom {@link SSLSessionVerifier} verifier. * @param verifier the custom {@link SSLSessionVerifier}. * @since 5.2 */ public ConscryptClientTlsStrategy(final SSLSessionVerifier verifier) { this(SSLContexts.createSystemDefault(), verifier); } @Override public void upgrade( final TransportSecurityLayer tlsSession, final NamedEndpoint endpoint, final Object attachment, final Timeout handshakeTimeout, final FutureCallback callback) { tlsSession.startTls( sslContext, endpoint, sslBufferMode, ConscryptSupport.initialize(attachment, initializer), ConscryptSupport.verify(verifier), handshakeTimeout, callback); } /** * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)} */ @Deprecated @Override public boolean upgrade( final TransportSecurityLayer tlsSession, final HttpHost host, final SocketAddress localAddress, final SocketAddress remoteAddress, final Object attachment, final Timeout handshakeTimeout) { final String scheme = host != null ? host.getSchemeName() : null; if (URIScheme.HTTPS.same(scheme)) { upgrade(tlsSession, host, attachment, handshakeTimeout, null); return true; } return false; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ConscryptServerTlsStrategy.java0100664 0000000 0000000 00000017336 14403631147 030211 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.ssl; import java.net.SocketAddress; import javax.net.ssl.SSLContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * Basic side-side implementation of {@link TlsStrategy} that upgrades to TLS for endpoints * with the specified local ports. * * @since 5.0 */ public class ConscryptServerTlsStrategy implements TlsStrategy { private final SSLContext sslContext; @SuppressWarnings("deprecation") private final org.apache.hc.core5.http.nio.ssl.SecurePortStrategy securePortStrategy; private final SSLBufferMode sslBufferMode; private final SSLSessionInitializer initializer; private final SSLSessionVerifier verifier; /** * @deprecated Use {@link ConscryptServerTlsStrategy#ConscryptServerTlsStrategy(SSLContext, SSLBufferMode, SSLSessionInitializer, SSLSessionVerifier)} */ @Deprecated public ConscryptServerTlsStrategy( final SSLContext sslContext, final org.apache.hc.core5.http.nio.ssl.SecurePortStrategy securePortStrategy, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this.sslContext = Args.notNull(sslContext, "SSL context"); this.securePortStrategy = securePortStrategy; this.sslBufferMode = sslBufferMode; this.initializer = initializer; this.verifier = verifier; } /** * @deprecated Use {@link ConscryptServerTlsStrategy#ConscryptServerTlsStrategy(SSLContext, SSLSessionInitializer, SSLSessionVerifier)} */ @Deprecated public ConscryptServerTlsStrategy( final SSLContext sslContext, final org.apache.hc.core5.http.nio.ssl.SecurePortStrategy securePortStrategy, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this(sslContext, securePortStrategy, null, initializer, verifier); } /** * @deprecated Use {@link ConscryptServerTlsStrategy#ConscryptServerTlsStrategy(SSLContext, SSLSessionVerifier)} */ @Deprecated public ConscryptServerTlsStrategy( final SSLContext sslContext, final org.apache.hc.core5.http.nio.ssl.SecurePortStrategy securePortStrategy, final SSLSessionVerifier verifier) { this(sslContext, securePortStrategy, null, null, verifier); } /** * @deprecated Use {@link ConscryptServerTlsStrategy#ConscryptServerTlsStrategy(SSLContext)} */ @Deprecated public ConscryptServerTlsStrategy(final SSLContext sslContext, final org.apache.hc.core5.http.nio.ssl.SecurePortStrategy securePortStrategy) { this(sslContext, securePortStrategy, null, null, null); } /** * @deprecated Use {@link ConscryptServerTlsStrategy#ConscryptServerTlsStrategy(SSLContext)} */ @Deprecated public ConscryptServerTlsStrategy(final SSLContext sslContext, final int... securePorts) { this(sslContext, new org.apache.hc.core5.http.nio.ssl.FixedPortStrategy(securePorts)); } public ConscryptServerTlsStrategy( final SSLContext sslContext, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this.sslContext = Args.notNull(sslContext, "SSL context"); this.sslBufferMode = sslBufferMode; this.initializer = initializer; this.verifier = verifier; this.securePortStrategy = null; } public ConscryptServerTlsStrategy( final SSLContext sslContext, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this(sslContext, (SSLBufferMode) null, initializer, verifier); } public ConscryptServerTlsStrategy(final SSLContext sslContext, final SSLSessionVerifier verifier) { this(sslContext, (SSLBufferMode) null, null, verifier); } public ConscryptServerTlsStrategy(final SSLContext sslContext) { this(sslContext, (SSLBufferMode) null, null, null); } /** * Empty constructor with the default SSL context based on system properties. * @see SSLContext * @since 5.2 */ public ConscryptServerTlsStrategy() { this(SSLContexts.createSystemDefault(), (SSLBufferMode) null, null, null); } /** * Constructor with the default SSL context based on system properties and custom {@link SSLSessionVerifier}. * @param verifier the custom {@link SSLSessionVerifier}. * @see SSLContext * @since 5.2 */ public ConscryptServerTlsStrategy(final SSLSessionVerifier verifier) { this(SSLContexts.createSystemDefault(), (SSLBufferMode) null, null, verifier); } private boolean isApplicable(final SocketAddress localAddress) { return securePortStrategy == null || securePortStrategy.isSecure(localAddress); } @Override public void upgrade( final TransportSecurityLayer tlsSession, final NamedEndpoint endpoint, final Object attachment, final Timeout handshakeTimeout, final FutureCallback callback) { tlsSession.startTls( sslContext, endpoint, sslBufferMode, ConscryptSupport.initialize(attachment, initializer), ConscryptSupport.verify(verifier), handshakeTimeout, callback); } /** * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)} */ @Deprecated @Override public boolean upgrade( final TransportSecurityLayer tlsSession, final HttpHost host, final SocketAddress localAddress, final SocketAddress remoteAddress, final Object attachment, final Timeout handshakeTimeout) { if (isApplicable(localAddress)) { upgrade(tlsSession, host, attachment, handshakeTimeout, null); return true; } return false; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ApplicationProtocol.java0100664 0000000 0000000 00000003000 14245617503 026600 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.ssl; /** * Supported application protocols. * * @since 5.0 */ public enum ApplicationProtocol { HTTP_2("h2"), HTTP_1_1("http/1.1"); public final String id; ApplicationProtocol(final String id) { this.id = id; } @Override public String toString() { return id; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ServerTlsStrategy.java0100664 0000000 0000000 00000015737 14403631147 026501 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.ssl; import java.net.SocketAddress; import javax.net.ssl.SSLContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * Basic side-side implementation of {@link TlsStrategy} that upgrades to TLS for endpoints * with the specified local ports. * * @since 5.0 */ public class H2ServerTlsStrategy implements TlsStrategy { private final SSLContext sslContext; @SuppressWarnings("deprecation") private final org.apache.hc.core5.http.nio.ssl.SecurePortStrategy securePortStrategy; private final SSLBufferMode sslBufferMode; private final SSLSessionInitializer initializer; private final SSLSessionVerifier verifier; /** * @deprecated Use {@link H2ServerTlsStrategy#H2ServerTlsStrategy(SSLContext, SSLBufferMode, SSLSessionInitializer, SSLSessionVerifier)} */ @Deprecated public H2ServerTlsStrategy( final SSLContext sslContext, final org.apache.hc.core5.http.nio.ssl.SecurePortStrategy securePortStrategy, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this.sslContext = Args.notNull(sslContext, "SSL context"); this.securePortStrategy = securePortStrategy; this.sslBufferMode = sslBufferMode; this.initializer = initializer; this.verifier = verifier; } /** * @deprecated Use {@link H2ServerTlsStrategy#H2ServerTlsStrategy(SSLContext, SSLSessionInitializer, SSLSessionVerifier)} */ @Deprecated public H2ServerTlsStrategy( final SSLContext sslContext, final org.apache.hc.core5.http.nio.ssl.SecurePortStrategy securePortStrategy, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this(sslContext, securePortStrategy, null, initializer, verifier); } /** * @deprecated Use {@link H2ServerTlsStrategy#H2ServerTlsStrategy(SSLContext, SSLSessionVerifier)} */ @Deprecated public H2ServerTlsStrategy( final SSLContext sslContext, final org.apache.hc.core5.http.nio.ssl.SecurePortStrategy securePortStrategy, final SSLSessionVerifier verifier) { this(sslContext, securePortStrategy, null, null, verifier); } /** * @deprecated Use {@link H2ServerTlsStrategy#H2ServerTlsStrategy(SSLContext)} */ @Deprecated public H2ServerTlsStrategy(final SSLContext sslContext, final org.apache.hc.core5.http.nio.ssl.SecurePortStrategy securePortStrategy) { this(sslContext, securePortStrategy, null, null, null); } /** * @deprecated Use {@link H2ServerTlsStrategy#H2ServerTlsStrategy()} */ @Deprecated public H2ServerTlsStrategy(final int... securePorts) { this(SSLContexts.createSystemDefault(), new org.apache.hc.core5.http.nio.ssl.FixedPortStrategy(securePorts)); } public H2ServerTlsStrategy( final SSLContext sslContext, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this.sslContext = Args.notNull(sslContext, "SSL context"); this.sslBufferMode = sslBufferMode; this.initializer = initializer; this.verifier = verifier; this.securePortStrategy = null; } public H2ServerTlsStrategy( final SSLContext sslContext, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { this(sslContext, (SSLBufferMode) null, initializer, verifier); } public H2ServerTlsStrategy(final SSLContext sslContext, final SSLSessionVerifier verifier) { this(sslContext, (SSLBufferMode) null, null, verifier); } public H2ServerTlsStrategy(final SSLContext sslContext) { this(sslContext, (SSLBufferMode) null, null, null); } public H2ServerTlsStrategy() { this(SSLContexts.createSystemDefault()); } private boolean isApplicable(final SocketAddress localAddress) { return securePortStrategy == null || securePortStrategy.isSecure(localAddress); } @Override public void upgrade( final TransportSecurityLayer tlsSession, final NamedEndpoint endpoint, final Object attachment, final Timeout handshakeTimeout, final FutureCallback callback) { tlsSession.startTls( sslContext, endpoint, sslBufferMode, H2TlsSupport.enforceRequirements(attachment, initializer), verifier, handshakeTimeout, callback); } /** * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)} */ @Deprecated @Override public boolean upgrade( final TransportSecurityLayer tlsSession, final HttpHost host, final SocketAddress localAddress, final SocketAddress remoteAddress, final Object attachment, final Timeout handshakeTimeout) { if (isApplicable(localAddress)) { upgrade(tlsSession, host, attachment, handshakeTimeout, null); return true; } return false; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ConscryptSupport.java0100664 0000000 0000000 00000006247 14403631147 026210 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.ssl; import javax.net.ssl.SSLParameters; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.http.ssl.TlsCiphers; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.conscrypt.Conscrypt; /** * Conscrypt TLS support methods * * @since 5.0 */ public final class ConscryptSupport { public static SSLSessionInitializer initialize( final Object attachment, final SSLSessionInitializer initializer) { return (endpoint, sslEngine) -> { final SSLParameters sslParameters = sslEngine.getSSLParameters(); sslParameters.setProtocols(TLS.excludeWeak(sslParameters.getProtocols())); sslParameters.setCipherSuites(TlsCiphers.excludeH2Blacklisted(sslParameters.getCipherSuites())); H2TlsSupport.setEnableRetransmissions(sslParameters, false); final String[] appProtocols = H2TlsSupport.selectApplicationProtocols(attachment); if (Conscrypt.isConscrypt(sslEngine)) { sslEngine.setSSLParameters(sslParameters); Conscrypt.setApplicationProtocols(sslEngine, appProtocols); } else { sslParameters.setApplicationProtocols(appProtocols); sslEngine.setSSLParameters(sslParameters); } if (initializer != null) { initializer.initialize(endpoint, sslEngine); } }; } public static SSLSessionVerifier verify(final SSLSessionVerifier verifier) { return (endpoint, sslEngine) -> { TlsDetails tlsDetails = verifier != null ? verifier.verify(endpoint, sslEngine) : null; if (tlsDetails == null && Conscrypt.isConscrypt(sslEngine)) { tlsDetails = new TlsDetails(sslEngine.getSession(), Conscrypt.getApplicationProtocol(sslEngine)); } return tlsDetails; }; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/HttpVersionPolicy.java0100664 0000000 0000000 00000002514 14245617503 025470 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2; /** * HTTP protocol version policy. * * @since 5.0 */ public enum HttpVersionPolicy { FORCE_HTTP_1, FORCE_HTTP_2, NEGOTIATE } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/H2TransportMetrics.java0100664 0000000 0000000 00000002661 14245617503 025543 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2; import org.apache.hc.core5.http.io.HttpTransportMetrics; /** * The point of access to connection statistics. * * @since 5.0 */ public interface H2TransportMetrics extends HttpTransportMetrics { long getFramesTransferred(); } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/H2StreamResetException.java0100664 0000000 0000000 00000004045 14403631147 026327 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2; import org.apache.hc.core5.http.HttpStreamResetException; import org.apache.hc.core5.util.Args; /** * Signals HTTP/2 protocol error that renders the actual HTTP/2 data stream * unreliable. * * @since 5.0 */ public class H2StreamResetException extends HttpStreamResetException { /** * Required for serialization support. * * @see java.io.Serializable */ private static final long serialVersionUID = 4280996898701236013L; private final int code; public H2StreamResetException(final H2Error error, final String message) { super(message); Args.notNull(error, "H2 Error code"); this.code = error.getCode(); } public H2StreamResetException(final int code, final String message) { super(message); this.code = code; } public int getCode() { return code; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/H2CorruptFrameException.java0100664 0000000 0000000 00000002751 14245617503 026510 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2; import java.io.IOException; /** * Signals corrupt HTTP/2 frame. * * @since 5.0 */ public class H2CorruptFrameException extends IOException { private static final long serialVersionUID = 1L; public H2CorruptFrameException(final String message) { super(message); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/H2PseudoResponseHeaders.java0100664 0000000 0000000 00000002612 14245617503 026466 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2; /** * Response pseudo HTTP headers defined by the HTTP/2 specification. * * @since 5.0 */ public final class H2PseudoResponseHeaders { public static final String STATUS = ":status"; } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/0040775 0000000 0000000 00000000000 14403631147 022116 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/package-info.java0100664 0000000 0000000 00000002401 14245617503 025303 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Default implementations of core HTTP/2 APIs. */ package org.apache.hc.core5.http2.impl; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/DefaultH2ResponseConverter.java0100664 0000000 0000000 00000013471 14403631147 030151 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http2.H2MessageConverter; import org.apache.hc.core5.http2.H2PseudoResponseHeaders; import org.apache.hc.core5.util.TextUtils; /** * HTTP/2 response converter. * * @since 5.0 */ public class DefaultH2ResponseConverter implements H2MessageConverter { public final static DefaultH2ResponseConverter INSTANCE = new DefaultH2ResponseConverter(); @Override public HttpResponse convert(final List

headers) throws HttpException { String statusText = null; final List
messageHeaders = new ArrayList<>(); for (int i = 0; i < headers.size(); i++) { final Header header = headers.get(i); final String name = header.getName(); final String value = header.getValue(); for (int n = 0; n < name.length(); n++) { final char ch = name.charAt(n); if (Character.isAlphabetic(ch) && !Character.isLowerCase(ch)) { throw new ProtocolException("Header name '%s' is invalid (header name contains uppercase characters)", name); } } if (name.startsWith(":")) { if (!messageHeaders.isEmpty()) { throw new ProtocolException("Invalid sequence of headers (pseudo-headers must precede message headers)"); } if (name.equals(H2PseudoResponseHeaders.STATUS)) { if (statusText != null) { throw new ProtocolException("Multiple '%s' response headers are illegal", name); } statusText = value; } else { throw new ProtocolException("Unsupported response header '%s'", name); } } else { if (name.equalsIgnoreCase(HttpHeaders.CONNECTION) || name.equalsIgnoreCase(HttpHeaders.KEEP_ALIVE) || name.equalsIgnoreCase(HttpHeaders.TRANSFER_ENCODING) || name.equalsIgnoreCase(HttpHeaders.UPGRADE)) { throw new ProtocolException("Header '%s: %s' is illegal for HTTP/2 messages", header.getName(), header.getValue()); } messageHeaders.add(header); } } if (statusText == null) { throw new ProtocolException("Mandatory response header '%s' not found", H2PseudoResponseHeaders.STATUS); } final int statusCode; try { statusCode = Integer.parseInt(statusText); } catch (final NumberFormatException ex) { throw new ProtocolException("Invalid response status: " + statusText); } final HttpResponse response = new BasicHttpResponse(statusCode, null); response.setVersion(HttpVersion.HTTP_2); for (int i = 0; i < messageHeaders.size(); i++) { response.addHeader(messageHeaders.get(i)); } return response; } @Override public List
convert(final HttpResponse message) throws HttpException { final int code = message.getCode(); if (code < 100 || code >= 600) { throw new ProtocolException("Response status %s is invalid", code); } final List
headers = new ArrayList<>(); headers.add(new BasicHeader(H2PseudoResponseHeaders.STATUS, Integer.toString(code), false)); for (final Iterator
it = message.headerIterator(); it.hasNext(); ) { final Header header = it.next(); final String name = header.getName(); final String value = header.getValue(); if (name.startsWith(":")) { throw new ProtocolException("Header name '%s' is invalid", name); } if (name.equalsIgnoreCase(HttpHeaders.CONNECTION) || name.equalsIgnoreCase(HttpHeaders.KEEP_ALIVE) || name.equalsIgnoreCase(HttpHeaders.TRANSFER_ENCODING) || name.equalsIgnoreCase(HttpHeaders.UPGRADE)) { throw new ProtocolException("Header '%s: %s' is illegal for HTTP/2 messages", header.getName(), header.getValue()); } headers.add(new BasicHeader(TextUtils.toLowerCase(name), value)); } return headers; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/BasicH2TransportMetrics.java0100664 0000000 0000000 00000003610 14245617503 027441 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; import org.apache.hc.core5.http2.H2TransportMetrics; /** * Default implementation of {@link H2TransportMetrics}. * * @since 5.0 */ public class BasicH2TransportMetrics extends BasicHttpTransportMetrics implements H2TransportMetrics { private final AtomicLong framesTransferred; public BasicH2TransportMetrics() { this.framesTransferred = new AtomicLong(0); } @Override public long getFramesTransferred() { return framesTransferred.get(); } public void incrementFramesTransferred() { framesTransferred.incrementAndGet(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/0040775 0000000 0000000 00000000000 14442305435 022704 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/package-info.java0100664 0000000 0000000 00000002472 14245617503 026100 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Default implementation of HTTP/2 transport based on * the asynchronous (non-blocking) I/O model. */ package org.apache.hc.core5.http2.impl.nio; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java0100664 0000000 0000000 00000213254 14442305435 031117 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.net.SocketAddress; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.charset.CharacterCodingException; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; import javax.net.ssl.SSLSession; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpStreamResetException; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.RequestNotExecutedException; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.impl.BasicEndpointDetails; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.impl.CharCodingSupport; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.command.ExecutableCommand; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.H2StreamResetException; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.config.H2Param; import org.apache.hc.core5.http2.config.H2Setting; import org.apache.hc.core5.http2.frame.FrameFactory; import org.apache.hc.core5.http2.frame.FrameFlag; import org.apache.hc.core5.http2.frame.FrameType; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.frame.StreamIdGenerator; import org.apache.hc.core5.http2.hpack.HPackDecoder; import org.apache.hc.core5.http2.hpack.HPackEncoder; import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics; import org.apache.hc.core5.http2.nio.AsyncPingHandler; import org.apache.hc.core5.http2.nio.command.PingCommand; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; import org.apache.hc.core5.util.Identifiable; import org.apache.hc.core5.util.Timeout; abstract class AbstractH2StreamMultiplexer implements Identifiable, HttpConnection { private static final long LINGER_TIME = 1000; // 1 second private static final long CONNECTION_WINDOW_LOW_MARK = 10 * 1024 * 1024; // 10 MiB enum ConnectionHandshake { READY, ACTIVE, GRACEFUL_SHUTDOWN, SHUTDOWN} enum SettingsHandshake { READY, TRANSMITTED, ACKED } private final ProtocolIOSession ioSession; private final FrameFactory frameFactory; private final StreamIdGenerator idGenerator; private final HttpProcessor httpProcessor; private final H2Config localConfig; private final BasicH2TransportMetrics inputMetrics; private final BasicH2TransportMetrics outputMetrics; private final BasicHttpConnectionMetrics connMetrics; private final FrameInputBuffer inputBuffer; private final FrameOutputBuffer outputBuffer; private final Deque outputQueue; private final HPackEncoder hPackEncoder; private final HPackDecoder hPackDecoder; private final Map streamMap; private final Queue pingHandlers; private final AtomicInteger connInputWindow; private final AtomicInteger connOutputWindow; private final AtomicInteger outputRequests; private final AtomicInteger lastStreamId; private final H2StreamListener streamListener; private ConnectionHandshake connState = ConnectionHandshake.READY; private SettingsHandshake localSettingState = SettingsHandshake.READY; private SettingsHandshake remoteSettingState = SettingsHandshake.READY; private int initInputWinSize; private int initOutputWinSize; private int lowMark; private volatile H2Config remoteConfig; private Continuation continuation; private int processedRemoteStreamId; private EndpointDetails endpointDetails; private boolean goAwayReceived; AbstractH2StreamMultiplexer( final ProtocolIOSession ioSession, final FrameFactory frameFactory, final StreamIdGenerator idGenerator, final HttpProcessor httpProcessor, final CharCodingConfig charCodingConfig, final H2Config h2Config, final H2StreamListener streamListener) { this.ioSession = Args.notNull(ioSession, "IO session"); this.frameFactory = Args.notNull(frameFactory, "Frame factory"); this.idGenerator = Args.notNull(idGenerator, "Stream id generator"); this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.localConfig = h2Config != null ? h2Config : H2Config.DEFAULT; this.inputMetrics = new BasicH2TransportMetrics(); this.outputMetrics = new BasicH2TransportMetrics(); this.connMetrics = new BasicHttpConnectionMetrics(this.inputMetrics, this.outputMetrics); this.inputBuffer = new FrameInputBuffer(this.inputMetrics, this.localConfig.getMaxFrameSize()); this.outputBuffer = new FrameOutputBuffer(this.outputMetrics, this.localConfig.getMaxFrameSize()); this.outputQueue = new ConcurrentLinkedDeque<>(); this.pingHandlers = new ConcurrentLinkedQueue<>(); this.outputRequests = new AtomicInteger(0); this.lastStreamId = new AtomicInteger(0); this.hPackEncoder = new HPackEncoder(CharCodingSupport.createEncoder(charCodingConfig)); this.hPackDecoder = new HPackDecoder(CharCodingSupport.createDecoder(charCodingConfig)); this.streamMap = new ConcurrentHashMap<>(); this.remoteConfig = H2Config.INIT; this.connInputWindow = new AtomicInteger(H2Config.INIT.getInitialWindowSize()); this.connOutputWindow = new AtomicInteger(H2Config.INIT.getInitialWindowSize()); this.initInputWinSize = H2Config.INIT.getInitialWindowSize(); this.initOutputWinSize = H2Config.INIT.getInitialWindowSize(); this.hPackDecoder.setMaxTableSize(H2Config.INIT.getHeaderTableSize()); this.hPackEncoder.setMaxTableSize(H2Config.INIT.getHeaderTableSize()); this.hPackDecoder.setMaxListSize(H2Config.INIT.getMaxHeaderListSize()); this.lowMark = H2Config.INIT.getInitialWindowSize() / 2; this.streamListener = streamListener; } @Override public String getId() { return ioSession.getId(); } abstract void acceptHeaderFrame() throws H2ConnectionException; abstract void acceptPushRequest() throws H2ConnectionException; abstract void acceptPushFrame() throws H2ConnectionException; abstract H2StreamHandler createRemotelyInitiatedStream( H2StreamChannel channel, HttpProcessor httpProcessor, BasicHttpConnectionMetrics connMetrics, HandlerFactory pushHandlerFactory) throws IOException; abstract H2StreamHandler createLocallyInitiatedStream( ExecutableCommand command, H2StreamChannel channel, HttpProcessor httpProcessor, BasicHttpConnectionMetrics connMetrics) throws IOException; private int updateWindow(final AtomicInteger window, final int delta) throws ArithmeticException { for (;;) { final int current = window.get(); long newValue = (long) current + delta; //TODO: work-around for what looks like a bug in Ngnix (1.11) // Tolerate if the update window exceeded by one if (newValue == 0x80000000L) { newValue = Integer.MAX_VALUE; } //TODO: needs to be removed if (Math.abs(newValue) > 0x7fffffffL) { throw new ArithmeticException("Update causes flow control window to exceed " + Integer.MAX_VALUE); } if (window.compareAndSet(current, (int) newValue)) { return (int) newValue; } } } private int updateInputWindow( final int streamId, final AtomicInteger window, final int delta) throws ArithmeticException { final int newSize = updateWindow(window, delta); if (streamListener != null) { streamListener.onInputFlowControl(this, streamId, delta, newSize); } return newSize; } private int updateOutputWindow( final int streamId, final AtomicInteger window, final int delta) throws ArithmeticException { final int newSize = updateWindow(window, delta); if (streamListener != null) { streamListener.onOutputFlowControl(this, streamId, delta, newSize); } return newSize; } private void commitFrameInternal(final RawFrame frame) throws IOException { if (outputBuffer.isEmpty() && outputQueue.isEmpty()) { if (streamListener != null) { streamListener.onFrameOutput(this, frame.getStreamId(), frame); } outputBuffer.write(frame, ioSession); } else { outputQueue.addLast(frame); } ioSession.setEvent(SelectionKey.OP_WRITE); } private void commitFrame(final RawFrame frame) throws IOException { Args.notNull(frame, "Frame"); ioSession.getLock().lock(); try { commitFrameInternal(frame); } finally { ioSession.getLock().unlock(); } } private void commitHeaders( final int streamId, final List headers, final boolean endStream) throws IOException { if (streamListener != null) { streamListener.onHeaderOutput(this, streamId, headers); } final ByteArrayBuffer buf = new ByteArrayBuffer(512); hPackEncoder.encodeHeaders(buf, headers, localConfig.isCompressionEnabled()); int off = 0; int remaining = buf.length(); boolean continuation = false; while (remaining > 0) { final int chunk = Math.min(remoteConfig.getMaxFrameSize(), remaining); final ByteBuffer payload = ByteBuffer.wrap(buf.array(), off, chunk); remaining -= chunk; off += chunk; final boolean endHeaders = remaining == 0; final RawFrame frame; if (!continuation) { frame = frameFactory.createHeaders(streamId, payload, endHeaders, endStream); continuation = true; } else { frame = frameFactory.createContinuation(streamId, payload, endHeaders); } commitFrameInternal(frame); } } private void commitPushPromise( final int streamId, final int promisedStreamId, final List
headers) throws IOException { if (headers == null || headers.isEmpty()) { throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Message headers are missing"); } if (streamListener != null) { streamListener.onHeaderOutput(this, streamId, headers); } final ByteArrayBuffer buf = new ByteArrayBuffer(512); buf.append((byte)(promisedStreamId >> 24)); buf.append((byte)(promisedStreamId >> 16)); buf.append((byte)(promisedStreamId >> 8)); buf.append((byte)(promisedStreamId)); hPackEncoder.encodeHeaders(buf, headers, localConfig.isCompressionEnabled()); int off = 0; int remaining = buf.length(); boolean continuation = false; while (remaining > 0) { final int chunk = Math.min(remoteConfig.getMaxFrameSize(), remaining); final ByteBuffer payload = ByteBuffer.wrap(buf.array(), off, chunk); remaining -= chunk; off += chunk; final boolean endHeaders = remaining == 0; final RawFrame frame; if (!continuation) { frame = frameFactory.createPushPromise(streamId, payload, endHeaders); continuation = true; } else { frame = frameFactory.createContinuation(streamId, payload, endHeaders); } commitFrameInternal(frame); } } private void streamDataFrame( final int streamId, final AtomicInteger streamOutputWindow, final ByteBuffer payload, final int chunk) throws IOException { final RawFrame dataFrame = frameFactory.createData(streamId, payload, false); if (streamListener != null) { streamListener.onFrameOutput(this, streamId, dataFrame); } updateOutputWindow(0, connOutputWindow, -chunk); updateOutputWindow(streamId, streamOutputWindow, -chunk); outputBuffer.write(dataFrame, ioSession); } private int streamData( final int streamId, final AtomicInteger streamOutputWindow, final ByteBuffer payload) throws IOException { if (outputBuffer.isEmpty() && outputQueue.isEmpty()) { final int capacity = Math.min(connOutputWindow.get(), streamOutputWindow.get()); if (capacity <= 0) { return 0; } final int maxPayloadSize = Math.min(capacity, remoteConfig.getMaxFrameSize()); final int chunk; if (payload.remaining() <= maxPayloadSize) { chunk = payload.remaining(); streamDataFrame(streamId, streamOutputWindow, payload, chunk); } else { chunk = maxPayloadSize; final int originalLimit = payload.limit(); try { payload.limit(payload.position() + chunk); streamDataFrame(streamId, streamOutputWindow, payload, chunk); } finally { payload.limit(originalLimit); } } payload.position(payload.position() + chunk); ioSession.setEvent(SelectionKey.OP_WRITE); return chunk; } return 0; } private void incrementInputCapacity( final int streamId, final AtomicInteger inputWindow, final int inputCapacity) throws IOException { if (inputCapacity > 0) { final int streamWinSize = inputWindow.get(); final int remainingCapacity = Integer.MAX_VALUE - streamWinSize; final int chunk = Math.min(inputCapacity, remainingCapacity); if (chunk != 0) { final RawFrame windowUpdateFrame = frameFactory.createWindowUpdate(streamId, chunk); commitFrame(windowUpdateFrame); updateInputWindow(streamId, inputWindow, chunk); } } } private void requestSessionOutput() { outputRequests.incrementAndGet(); ioSession.setEvent(SelectionKey.OP_WRITE); } private void updateLastStreamId(final int streamId) { final int currentId = lastStreamId.get(); if (streamId > currentId) { lastStreamId.compareAndSet(currentId, streamId); } } private int generateStreamId() { for (;;) { final int currentId = lastStreamId.get(); final int newStreamId = idGenerator.generate(currentId); if (lastStreamId.compareAndSet(currentId, newStreamId)) { return newStreamId; } } } public final void onConnect() throws HttpException, IOException { connState = ConnectionHandshake.ACTIVE; final RawFrame settingsFrame = frameFactory.createSettings( new H2Setting(H2Param.HEADER_TABLE_SIZE, localConfig.getHeaderTableSize()), new H2Setting(H2Param.ENABLE_PUSH, localConfig.isPushEnabled() ? 1 : 0), new H2Setting(H2Param.MAX_CONCURRENT_STREAMS, localConfig.getMaxConcurrentStreams()), new H2Setting(H2Param.INITIAL_WINDOW_SIZE, localConfig.getInitialWindowSize()), new H2Setting(H2Param.MAX_FRAME_SIZE, localConfig.getMaxFrameSize()), new H2Setting(H2Param.MAX_HEADER_LIST_SIZE, localConfig.getMaxHeaderListSize())); commitFrame(settingsFrame); localSettingState = SettingsHandshake.TRANSMITTED; maximizeConnWindow(connInputWindow.get()); if (streamListener != null) { final int initInputWindow = connInputWindow.get(); streamListener.onInputFlowControl(this, 0, initInputWindow, initInputWindow); final int initOutputWindow = connOutputWindow.get(); streamListener.onOutputFlowControl(this, 0, initOutputWindow, initOutputWindow); } } public final void onInput(final ByteBuffer src) throws HttpException, IOException { if (connState == ConnectionHandshake.SHUTDOWN) { ioSession.clearEvent(SelectionKey.OP_READ); } else { for (;;) { final RawFrame frame = inputBuffer.read(src, ioSession); if (frame == null) { break; } if (streamListener != null) { streamListener.onFrameInput(this, frame.getStreamId(), frame); } consumeFrame(frame); } } } public final void onOutput() throws HttpException, IOException { ioSession.getLock().lock(); try { if (!outputBuffer.isEmpty()) { outputBuffer.flush(ioSession); } while (outputBuffer.isEmpty()) { final RawFrame frame = outputQueue.poll(); if (frame != null) { if (streamListener != null) { streamListener.onFrameOutput(this, frame.getStreamId(), frame); } outputBuffer.write(frame, ioSession); } else { break; } } } finally { ioSession.getLock().unlock(); } if (connState.compareTo(ConnectionHandshake.SHUTDOWN) < 0) { if (connOutputWindow.get() > 0 && remoteSettingState == SettingsHandshake.ACKED) { produceOutput(); } final int pendingOutputRequests = outputRequests.get(); boolean outputPending = false; if (!streamMap.isEmpty() && connOutputWindow.get() > 0) { for (final Iterator> it = streamMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final H2Stream stream = entry.getValue(); if (!stream.isLocalClosed() && stream.getOutputWindow().get() > 0 && stream.isOutputReady()) { outputPending = true; break; } } } ioSession.getLock().lock(); try { if (!outputPending && outputBuffer.isEmpty() && outputQueue.isEmpty() && outputRequests.compareAndSet(pendingOutputRequests, 0)) { ioSession.clearEvent(SelectionKey.OP_WRITE); } else { outputRequests.addAndGet(-pendingOutputRequests); } } finally { ioSession.getLock().unlock(); } } if (connState.compareTo(ConnectionHandshake.ACTIVE) <= 0 && remoteSettingState == SettingsHandshake.ACKED) { processPendingCommands(); } if (connState.compareTo(ConnectionHandshake.GRACEFUL_SHUTDOWN) == 0) { int liveStreams = 0; for (final Iterator> it = streamMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final H2Stream stream = entry.getValue(); if (stream.isLocalClosed() && stream.isRemoteClosed()) { stream.releaseResources(); it.remove(); } else { if (idGenerator.isSameSide(stream.getId()) || stream.getId() <= processedRemoteStreamId) { liveStreams++; } } } if (liveStreams == 0) { connState = ConnectionHandshake.SHUTDOWN; } } if (connState.compareTo(ConnectionHandshake.SHUTDOWN) >= 0) { if (!streamMap.isEmpty()) { for (final H2Stream stream : streamMap.values()) { stream.releaseResources(); } streamMap.clear(); } ioSession.getLock().lock(); try { if (outputBuffer.isEmpty() && outputQueue.isEmpty()) { ioSession.close(); } } finally { ioSession.getLock().unlock(); } } } public final void onTimeout(final Timeout timeout) throws HttpException, IOException { connState = ConnectionHandshake.SHUTDOWN; final RawFrame goAway; if (localSettingState != SettingsHandshake.ACKED) { goAway = frameFactory.createGoAway(processedRemoteStreamId, H2Error.SETTINGS_TIMEOUT, "Setting timeout (" + timeout + ")"); } else { goAway = frameFactory.createGoAway(processedRemoteStreamId, H2Error.NO_ERROR, "Timeout due to inactivity (" + timeout + ")"); } commitFrame(goAway); for (final Iterator> it = streamMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final H2Stream stream = entry.getValue(); stream.reset(new H2StreamResetException(H2Error.NO_ERROR, "Timeout due to inactivity (" + timeout + ")")); } streamMap.clear(); } public final void onDisconnect() { for (;;) { final AsyncPingHandler pingHandler = pingHandlers.poll(); if (pingHandler != null) { pingHandler.cancel(); } else { break; } } for (final Iterator> it = streamMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final H2Stream stream = entry.getValue(); stream.cancel(); } for (;;) { final Command command = ioSession.poll(); if (command != null) { if (command instanceof ExecutableCommand) { ((ExecutableCommand) command).failed(new ConnectionClosedException()); } else { command.cancel(); } } else { break; } } } private void processPendingCommands() throws IOException, HttpException { while (streamMap.size() < remoteConfig.getMaxConcurrentStreams()) { final Command command = ioSession.poll(); if (command == null) { break; } if (command instanceof ShutdownCommand) { final ShutdownCommand shutdownCommand = (ShutdownCommand) command; if (shutdownCommand.getType() == CloseMode.IMMEDIATE) { for (final Iterator> it = streamMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final H2Stream stream = entry.getValue(); stream.cancel(); } streamMap.clear(); connState = ConnectionHandshake.SHUTDOWN; } else { if (connState.compareTo(ConnectionHandshake.ACTIVE) <= 0) { final RawFrame goAway = frameFactory.createGoAway(processedRemoteStreamId, H2Error.NO_ERROR, "Graceful shutdown"); commitFrame(goAway); connState = streamMap.isEmpty() ? ConnectionHandshake.SHUTDOWN : ConnectionHandshake.GRACEFUL_SHUTDOWN; } } break; } else if (command instanceof PingCommand) { final PingCommand pingCommand = (PingCommand) command; final AsyncPingHandler handler = pingCommand.getHandler(); pingHandlers.add(handler); final RawFrame ping = frameFactory.createPing(handler.getData()); commitFrame(ping); } else if (command instanceof ExecutableCommand) { final int streamId = generateStreamId(); final H2StreamChannelImpl channel = new H2StreamChannelImpl( streamId, true, initInputWinSize, initOutputWinSize); final ExecutableCommand executableCommand = (ExecutableCommand) command; final H2StreamHandler streamHandler = createLocallyInitiatedStream( executableCommand, channel, httpProcessor, connMetrics); final H2Stream stream = new H2Stream(channel, streamHandler, false); streamMap.put(streamId, stream); if (streamListener != null) { final int initInputWindow = stream.getInputWindow().get(); streamListener.onInputFlowControl(this, streamId, initInputWindow, initInputWindow); final int initOutputWindow = stream.getOutputWindow().get(); streamListener.onOutputFlowControl(this, streamId, initOutputWindow, initOutputWindow); } if (stream.isOutputReady()) { stream.produceOutput(); } final CancellableDependency cancellableDependency = executableCommand.getCancellableDependency(); if (cancellableDependency != null) { cancellableDependency.setDependency(stream::abort); } if (!outputQueue.isEmpty()) { return; } } } } public final void onException(final Exception cause) { try { for (;;) { final AsyncPingHandler pingHandler = pingHandlers.poll(); if (pingHandler != null) { pingHandler.failed(cause); } else { break; } } for (;;) { final Command command = ioSession.poll(); if (command != null) { if (command instanceof ExecutableCommand) { ((ExecutableCommand) command).failed(new ConnectionClosedException()); } else { command.cancel(); } } else { break; } } for (final Iterator> it = streamMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final H2Stream stream = entry.getValue(); stream.reset(cause); } streamMap.clear(); if (!(cause instanceof ConnectionClosedException)) { if (connState.compareTo(ConnectionHandshake.GRACEFUL_SHUTDOWN) <= 0) { final H2Error errorCode; if (cause instanceof H2ConnectionException) { errorCode = H2Error.getByCode(((H2ConnectionException) cause).getCode()); } else if (cause instanceof ProtocolException){ errorCode = H2Error.PROTOCOL_ERROR; } else { errorCode = H2Error.INTERNAL_ERROR; } final RawFrame goAway = frameFactory.createGoAway(processedRemoteStreamId, errorCode, cause.getMessage()); commitFrame(goAway); } } } catch (final IOException ignore) { } finally { connState = ConnectionHandshake.SHUTDOWN; final CloseMode closeMode; if (cause instanceof ConnectionClosedException) { closeMode = CloseMode.GRACEFUL; } else if (cause instanceof IOException) { closeMode = CloseMode.IMMEDIATE; } else { closeMode = CloseMode.GRACEFUL; } ioSession.close(closeMode); } } private H2Stream getValidStream(final int streamId) throws H2ConnectionException { if (streamId == 0) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id: " + streamId); } final H2Stream stream = streamMap.get(streamId); if (stream == null) { if (streamId <= lastStreamId.get()) { throw new H2ConnectionException(H2Error.STREAM_CLOSED, "Stream closed"); } else { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected stream id: " + streamId); } } return stream; } private void consumeFrame(final RawFrame frame) throws HttpException, IOException { final FrameType frameType = FrameType.valueOf(frame.getType()); final int streamId = frame.getStreamId(); if (continuation != null && frameType != FrameType.CONTINUATION) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "CONTINUATION frame expected"); } switch (frameType) { case DATA: { final H2Stream stream = getValidStream(streamId); try { consumeDataFrame(frame, stream); } catch (final H2StreamResetException ex) { stream.localReset(ex); } catch (final HttpStreamResetException ex) { stream.localReset(ex, ex.getCause() != null ? H2Error.INTERNAL_ERROR : H2Error.CANCEL); } if (stream.isTerminated()) { streamMap.remove(streamId); stream.releaseResources(); requestSessionOutput(); } } break; case HEADERS: { if (streamId == 0) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id: " + streamId); } H2Stream stream = streamMap.get(streamId); if (stream == null) { acceptHeaderFrame(); if (idGenerator.isSameSide(streamId)) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id: " + streamId); } if (goAwayReceived ) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "GOAWAY received"); } updateLastStreamId(streamId); final H2StreamChannelImpl channel = new H2StreamChannelImpl( streamId, false, initInputWinSize, initOutputWinSize); final H2StreamHandler streamHandler; if (connState.compareTo(ConnectionHandshake.ACTIVE) <= 0) { streamHandler = createRemotelyInitiatedStream(channel, httpProcessor, connMetrics, null); } else { streamHandler = NoopH2StreamHandler.INSTANCE; channel.setLocalEndStream(); } stream = new H2Stream(channel, streamHandler, true); if (stream.isOutputReady()) { stream.produceOutput(); } streamMap.put(streamId, stream); } try { consumeHeaderFrame(frame, stream); if (stream.isOutputReady()) { stream.produceOutput(); } } catch (final H2StreamResetException ex) { stream.localReset(ex); } catch (final HttpStreamResetException ex) { stream.localReset(ex, ex.getCause() != null ? H2Error.INTERNAL_ERROR : H2Error.CANCEL); } catch (final HttpException ex) { stream.handle(ex); } if (stream.isTerminated()) { streamMap.remove(streamId); stream.releaseResources(); requestSessionOutput(); } } break; case CONTINUATION: { if (continuation == null) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected CONTINUATION frame"); } if (streamId != continuation.streamId) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected CONTINUATION stream id: " + streamId); } final H2Stream stream = getValidStream(streamId); try { consumeContinuationFrame(frame, stream); } catch (final H2StreamResetException ex) { stream.localReset(ex); } catch (final HttpStreamResetException ex) { stream.localReset(ex, ex.getCause() != null ? H2Error.INTERNAL_ERROR : H2Error.CANCEL); } if (stream.isTerminated()) { streamMap.remove(streamId); stream.releaseResources(); requestSessionOutput(); } } break; case WINDOW_UPDATE: { final ByteBuffer payload = frame.getPayload(); if (payload == null || payload.remaining() != 4) { throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid WINDOW_UPDATE frame payload"); } final int delta = payload.getInt(); if (delta <= 0) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Invalid WINDOW_UPDATE delta"); } if (streamId == 0) { try { updateOutputWindow(0, connOutputWindow, delta); } catch (final ArithmeticException ex) { throw new H2ConnectionException(H2Error.FLOW_CONTROL_ERROR, ex.getMessage()); } } else { final H2Stream stream = streamMap.get(streamId); if (stream != null) { try { updateOutputWindow(streamId, stream.getOutputWindow(), delta); } catch (final ArithmeticException ex) { throw new H2ConnectionException(H2Error.FLOW_CONTROL_ERROR, ex.getMessage()); } } } ioSession.setEvent(SelectionKey.OP_WRITE); } break; case RST_STREAM: { if (streamId == 0) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id: " + streamId); } final H2Stream stream = streamMap.get(streamId); if (stream == null) { if (streamId > lastStreamId.get()) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected stream id: " + streamId); } } else { final ByteBuffer payload = frame.getPayload(); if (payload == null || payload.remaining() != 4) { throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid RST_STREAM frame payload"); } final int errorCode = payload.getInt(); stream.reset(new H2StreamResetException(errorCode, "Stream reset (" + errorCode + ")")); streamMap.remove(streamId); stream.releaseResources(); requestSessionOutput(); } } break; case PING: { if (streamId != 0) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id"); } final ByteBuffer ping = frame.getPayloadContent(); if (ping == null || ping.remaining() != 8) { throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid PING frame payload"); } if (frame.isFlagSet(FrameFlag.ACK)) { final AsyncPingHandler pingHandler = pingHandlers.poll(); if (pingHandler != null) { pingHandler.consumeResponse(ping); } } else { final ByteBuffer pong = ByteBuffer.allocate(ping.remaining()); pong.put(ping); pong.flip(); final RawFrame response = frameFactory.createPingAck(pong); commitFrame(response); } } break; case SETTINGS: { if (streamId != 0) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id"); } if (frame.isFlagSet(FrameFlag.ACK)) { if (localSettingState == SettingsHandshake.TRANSMITTED) { localSettingState = SettingsHandshake.ACKED; ioSession.setEvent(SelectionKey.OP_WRITE); applyLocalSettings(); } } else { final ByteBuffer payload = frame.getPayload(); if (payload != null) { if ((payload.remaining() % 6) != 0) { throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid SETTINGS payload"); } consumeSettingsFrame(payload); remoteSettingState = SettingsHandshake.TRANSMITTED; } // Send ACK final RawFrame response = frameFactory.createSettingsAck(); commitFrame(response); remoteSettingState = SettingsHandshake.ACKED; } } break; case PRIORITY: // Stream priority not supported break; case PUSH_PROMISE: { acceptPushFrame(); if (goAwayReceived ) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "GOAWAY received"); } if (!localConfig.isPushEnabled()) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Push is disabled"); } final H2Stream stream = getValidStream(streamId); if (stream.isRemoteClosed()) { stream.localReset(new H2StreamResetException(H2Error.STREAM_CLOSED, "Stream closed")); break; } final ByteBuffer payload = frame.getPayloadContent(); if (payload == null || payload.remaining() < 4) { throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid PUSH_PROMISE payload"); } final int promisedStreamId = payload.getInt(); if (promisedStreamId == 0 || idGenerator.isSameSide(promisedStreamId)) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal promised stream id: " + promisedStreamId); } if (streamMap.get(promisedStreamId) != null) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected promised stream id: " + promisedStreamId); } updateLastStreamId(promisedStreamId); final H2StreamChannelImpl channel = new H2StreamChannelImpl( promisedStreamId, false, initInputWinSize, initOutputWinSize); final H2StreamHandler streamHandler; if (connState.compareTo(ConnectionHandshake.ACTIVE) <= 0) { streamHandler = createRemotelyInitiatedStream(channel, httpProcessor, connMetrics, stream.getPushHandlerFactory()); } else { streamHandler = NoopH2StreamHandler.INSTANCE; channel.setLocalEndStream(); } final H2Stream promisedStream = new H2Stream(channel, streamHandler, true); streamMap.put(promisedStreamId, promisedStream); try { consumePushPromiseFrame(frame, payload, promisedStream); } catch (final H2StreamResetException ex) { promisedStream.localReset(ex); } catch (final HttpStreamResetException ex) { promisedStream.localReset(ex, ex.getCause() != null ? H2Error.INTERNAL_ERROR : H2Error.NO_ERROR); } } break; case GOAWAY: { if (streamId != 0) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id"); } final ByteBuffer payload = frame.getPayload(); if (payload == null || payload.remaining() < 8) { throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid GOAWAY payload"); } final int processedLocalStreamId = payload.getInt(); final int errorCode = payload.getInt(); goAwayReceived = true; if (errorCode == H2Error.NO_ERROR.getCode()) { if (connState.compareTo(ConnectionHandshake.ACTIVE) <= 0) { for (final Iterator> it = streamMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final int activeStreamId = entry.getKey(); if (!idGenerator.isSameSide(activeStreamId) && activeStreamId > processedLocalStreamId) { final H2Stream stream = entry.getValue(); stream.cancel(); it.remove(); } } } connState = streamMap.isEmpty() ? ConnectionHandshake.SHUTDOWN : ConnectionHandshake.GRACEFUL_SHUTDOWN; } else { for (final Iterator> it = streamMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final H2Stream stream = entry.getValue(); stream.reset(new H2StreamResetException(errorCode, "Connection terminated by the peer (" + errorCode + ")")); } streamMap.clear(); connState = ConnectionHandshake.SHUTDOWN; } } ioSession.setEvent(SelectionKey.OP_WRITE); break; } } private void consumeDataFrame(final RawFrame frame, final H2Stream stream) throws HttpException, IOException { final int streamId = stream.getId(); final ByteBuffer payload = frame.getPayloadContent(); if (payload != null) { final int frameLength = frame.getLength(); final int streamWinSize = updateInputWindow(streamId, stream.getInputWindow(), -frameLength); if (streamWinSize < lowMark && !stream.isRemoteClosed()) { stream.produceInputCapacityUpdate(); } final int connWinSize = updateInputWindow(0, connInputWindow, -frameLength); if (connWinSize < CONNECTION_WINDOW_LOW_MARK) { maximizeConnWindow(connWinSize); } } if (stream.isRemoteClosed()) { throw new H2StreamResetException(H2Error.STREAM_CLOSED, "Stream already closed"); } if (frame.isFlagSet(FrameFlag.END_STREAM)) { stream.setRemoteEndStream(); } if (stream.isLocalReset()) { return; } stream.consumeData(payload); } private void maximizeConnWindow(final int connWinSize) throws IOException { final int delta = Integer.MAX_VALUE - connWinSize; if (delta > 0) { final RawFrame windowUpdateFrame = frameFactory.createWindowUpdate(0, delta); commitFrame(windowUpdateFrame); updateInputWindow(0, connInputWindow, delta); } } private void consumePushPromiseFrame(final RawFrame frame, final ByteBuffer payload, final H2Stream promisedStream) throws HttpException, IOException { final int promisedStreamId = promisedStream.getId(); if (!frame.isFlagSet(FrameFlag.END_HEADERS)) { continuation = new Continuation(promisedStreamId, frame.getType(), true); } if (continuation == null) { final List
headers = hPackDecoder.decodeHeaders(payload); if (promisedStreamId > processedRemoteStreamId) { processedRemoteStreamId = promisedStreamId; } if (streamListener != null) { streamListener.onHeaderInput(this, promisedStreamId, headers); } promisedStream.consumePromise(headers); } else { continuation.copyPayload(payload); } } List
decodeHeaders(final ByteBuffer payload) throws HttpException { return hPackDecoder.decodeHeaders(payload); } private void consumeHeaderFrame(final RawFrame frame, final H2Stream stream) throws HttpException, IOException { final int streamId = stream.getId(); if (!frame.isFlagSet(FrameFlag.END_HEADERS)) { continuation = new Continuation(streamId, frame.getType(), frame.isFlagSet(FrameFlag.END_STREAM)); } final ByteBuffer payload = frame.getPayloadContent(); if (frame.isFlagSet(FrameFlag.PRIORITY)) { // Priority not supported payload.getInt(); payload.get(); } if (continuation == null) { final List
headers = decodeHeaders(payload); if (stream.isRemoteInitiated() && streamId > processedRemoteStreamId) { processedRemoteStreamId = streamId; } if (streamListener != null) { streamListener.onHeaderInput(this, streamId, headers); } if (stream.isRemoteClosed()) { throw new H2StreamResetException(H2Error.STREAM_CLOSED, "Stream already closed"); } if (stream.isLocalReset()) { return; } if (frame.isFlagSet(FrameFlag.END_STREAM)) { stream.setRemoteEndStream(); } stream.consumeHeader(headers); } else { continuation.copyPayload(payload); } } private void consumeContinuationFrame(final RawFrame frame, final H2Stream stream) throws HttpException, IOException { final int streamId = frame.getStreamId(); final ByteBuffer payload = frame.getPayload(); continuation.copyPayload(payload); if (frame.isFlagSet(FrameFlag.END_HEADERS)) { final List
headers = decodeHeaders(continuation.getContent()); if (stream.isRemoteInitiated() && streamId > processedRemoteStreamId) { processedRemoteStreamId = streamId; } if (streamListener != null) { streamListener.onHeaderInput(this, streamId, headers); } if (stream.isRemoteClosed()) { throw new H2StreamResetException(H2Error.STREAM_CLOSED, "Stream already closed"); } if (stream.isLocalReset()) { return; } if (continuation.endStream) { stream.setRemoteEndStream(); } if (continuation.type == FrameType.PUSH_PROMISE.getValue()) { stream.consumePromise(headers); } else { stream.consumeHeader(headers); } continuation = null; } } private void consumeSettingsFrame(final ByteBuffer payload) throws HttpException, IOException { final H2Config.Builder configBuilder = H2Config.initial(); while (payload.hasRemaining()) { final int code = payload.getShort(); final int value = payload.getInt(); final H2Param param = H2Param.valueOf(code); if (param != null) { switch (param) { case HEADER_TABLE_SIZE: try { configBuilder.setHeaderTableSize(value); } catch (final IllegalArgumentException ex) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, ex.getMessage()); } break; case MAX_CONCURRENT_STREAMS: try { configBuilder.setMaxConcurrentStreams(value); } catch (final IllegalArgumentException ex) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, ex.getMessage()); } break; case ENABLE_PUSH: configBuilder.setPushEnabled(value == 1); break; case INITIAL_WINDOW_SIZE: try { configBuilder.setInitialWindowSize(value); } catch (final IllegalArgumentException ex) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, ex.getMessage()); } break; case MAX_FRAME_SIZE: try { configBuilder.setMaxFrameSize(value); } catch (final IllegalArgumentException ex) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, ex.getMessage()); } break; case MAX_HEADER_LIST_SIZE: try { configBuilder.setMaxHeaderListSize(value); } catch (final IllegalArgumentException ex) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, ex.getMessage()); } break; } } } applyRemoteSettings(configBuilder.build()); } private void produceOutput() throws HttpException, IOException { for (final Iterator> it = streamMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final H2Stream stream = entry.getValue(); if (!stream.isLocalClosed() && stream.getOutputWindow().get() > 0) { stream.produceOutput(); } if (stream.isTerminated()) { it.remove(); stream.releaseResources(); requestSessionOutput(); } if (!outputQueue.isEmpty()) { break; } } } private void applyRemoteSettings(final H2Config config) throws H2ConnectionException { remoteConfig = config; hPackEncoder.setMaxTableSize(remoteConfig.getHeaderTableSize()); final int delta = remoteConfig.getInitialWindowSize() - initOutputWinSize; initOutputWinSize = remoteConfig.getInitialWindowSize(); final int maxFrameSize = remoteConfig.getMaxFrameSize(); if (maxFrameSize < outputBuffer.getMaxFramePayloadSize()) { try { outputBuffer.resize(maxFrameSize); } catch (final BufferOverflowException ex) { throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Failure resizing the frame output buffer"); } } if (delta != 0) { if (!streamMap.isEmpty()) { for (final Iterator> it = streamMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final H2Stream stream = entry.getValue(); try { updateOutputWindow(stream.getId(), stream.getOutputWindow(), delta); } catch (final ArithmeticException ex) { throw new H2ConnectionException(H2Error.FLOW_CONTROL_ERROR, ex.getMessage()); } } } } } private void applyLocalSettings() throws H2ConnectionException { hPackDecoder.setMaxTableSize(localConfig.getHeaderTableSize()); hPackDecoder.setMaxListSize(localConfig.getMaxHeaderListSize()); final int delta = localConfig.getInitialWindowSize() - initInputWinSize; initInputWinSize = localConfig.getInitialWindowSize(); if (delta != 0 && !streamMap.isEmpty()) { for (final Iterator> it = streamMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final H2Stream stream = entry.getValue(); try { updateInputWindow(stream.getId(), stream.getInputWindow(), delta); } catch (final ArithmeticException ex) { throw new H2ConnectionException(H2Error.FLOW_CONTROL_ERROR, ex.getMessage()); } } } lowMark = initInputWinSize / 2; } @Override public void close() throws IOException { ioSession.enqueue(ShutdownCommand.GRACEFUL, Command.Priority.IMMEDIATE); } @Override public void close(final CloseMode closeMode) { ioSession.close(closeMode); } @Override public boolean isOpen() { return connState == ConnectionHandshake.ACTIVE; } @Override public void setSocketTimeout(final Timeout timeout) { ioSession.setSocketTimeout(timeout); } @Override public SSLSession getSSLSession() { final TlsDetails tlsDetails = ioSession.getTlsDetails(); return tlsDetails != null ? tlsDetails.getSSLSession() : null; } @Override public EndpointDetails getEndpointDetails() { if (endpointDetails == null) { endpointDetails = new BasicEndpointDetails( ioSession.getRemoteAddress(), ioSession.getLocalAddress(), connMetrics, ioSession.getSocketTimeout()); } return endpointDetails; } @Override public Timeout getSocketTimeout() { return ioSession.getSocketTimeout(); } @Override public ProtocolVersion getProtocolVersion() { return HttpVersion.HTTP_2; } @Override public SocketAddress getRemoteAddress() { return ioSession.getRemoteAddress(); } @Override public SocketAddress getLocalAddress() { return ioSession.getLocalAddress(); } void appendState(final StringBuilder buf) { buf.append("connState=").append(connState) .append(", connInputWindow=").append(connInputWindow) .append(", connOutputWindow=").append(connOutputWindow) .append(", outputQueue=").append(outputQueue.size()) .append(", streamMap=").append(streamMap.size()) .append(", processedRemoteStreamId=").append(processedRemoteStreamId); } private static class Continuation { final int streamId; final int type; final boolean endStream; final ByteArrayBuffer headerBuffer; private Continuation(final int streamId, final int type, final boolean endStream) { this.streamId = streamId; this.type = type; this.endStream = endStream; this.headerBuffer = new ByteArrayBuffer(1024); } void copyPayload(final ByteBuffer payload) { if (payload == null) { return; } headerBuffer.ensureCapacity(payload.remaining()); payload.get(headerBuffer.array(), headerBuffer.length(), payload.remaining()); } ByteBuffer getContent() { return ByteBuffer.wrap(headerBuffer.array(), 0, headerBuffer.length()); } } private class H2StreamChannelImpl implements H2StreamChannel { private final int id; private final AtomicInteger inputWindow; private final AtomicInteger outputWindow; private volatile boolean idle; private volatile boolean remoteEndStream; private volatile boolean localEndStream; private volatile long deadline; H2StreamChannelImpl(final int id, final boolean idle, final int initialInputWindowSize, final int initialOutputWindowSize) { this.id = id; this.idle = idle; this.inputWindow = new AtomicInteger(initialInputWindowSize); this.outputWindow = new AtomicInteger(initialOutputWindowSize); } int getId() { return id; } AtomicInteger getOutputWindow() { return outputWindow; } AtomicInteger getInputWindow() { return inputWindow; } @Override public void submit(final List
headers, final boolean endStream) throws IOException { ioSession.getLock().lock(); try { if (headers == null || headers.isEmpty()) { throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Message headers are missing"); } if (localEndStream) { return; } idle = false; commitHeaders(id, headers, endStream); if (endStream) { localEndStream = true; } } finally { ioSession.getLock().unlock(); } } @Override public void push(final List
headers, final AsyncPushProducer pushProducer) throws HttpException, IOException { acceptPushRequest(); final int promisedStreamId = generateStreamId(); final H2StreamChannelImpl channel = new H2StreamChannelImpl( promisedStreamId, true, localConfig.getInitialWindowSize(), remoteConfig.getInitialWindowSize()); final HttpCoreContext context = HttpCoreContext.create(); context.setAttribute(HttpCoreContext.SSL_SESSION, getSSLSession()); context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, getEndpointDetails()); final H2StreamHandler streamHandler = new ServerPushH2StreamHandler( channel, httpProcessor, connMetrics, pushProducer, context); final H2Stream stream = new H2Stream(channel, streamHandler, false); streamMap.put(promisedStreamId, stream); ioSession.getLock().lock(); try { if (localEndStream) { stream.releaseResources(); return; } commitPushPromise(id, promisedStreamId, headers); idle = false; } finally { ioSession.getLock().unlock(); } } @Override public void update(final int increment) throws IOException { if (remoteEndStream) { return; } incrementInputCapacity(0, connInputWindow, increment); incrementInputCapacity(id, inputWindow, increment); } @Override public int write(final ByteBuffer payload) throws IOException { ioSession.getLock().lock(); try { if (localEndStream) { return 0; } return streamData(id, outputWindow, payload); } finally { ioSession.getLock().unlock(); } } @Override public void endStream(final List trailers) throws IOException { ioSession.getLock().lock(); try { if (localEndStream) { return; } localEndStream = true; if (trailers != null && !trailers.isEmpty()) { commitHeaders(id, trailers, true); } else { final RawFrame frame = frameFactory.createData(id, null, true); commitFrameInternal(frame); } } finally { ioSession.getLock().unlock(); } } @Override public void endStream() throws IOException { endStream(null); } @Override public void requestOutput() { requestSessionOutput(); } boolean isRemoteClosed() { return remoteEndStream; } void setRemoteEndStream() { remoteEndStream = true; } boolean isLocalClosed() { return localEndStream; } void setLocalEndStream() { localEndStream = true; } boolean isLocalReset() { return deadline > 0; } boolean isResetDeadline() { final long l = deadline; return l > 0 && l < System.currentTimeMillis(); } boolean localReset(final int code) throws IOException { ioSession.getLock().lock(); try { if (localEndStream) { return false; } localEndStream = true; deadline = System.currentTimeMillis() + LINGER_TIME; if (!idle) { final RawFrame resetStream = frameFactory.createResetStream(id, code); commitFrameInternal(resetStream); return true; } return false; } finally { ioSession.getLock().unlock(); } } boolean localReset(final H2Error error) throws IOException { return localReset(error!= null ? error.getCode() : H2Error.INTERNAL_ERROR.getCode()); } @Override public boolean cancel() { try { return localReset(H2Error.CANCEL); } catch (final IOException ignore) { return false; } } void appendState(final StringBuilder buf) { buf.append("id=").append(id) .append(", connState=").append(connState) .append(", inputWindow=").append(inputWindow) .append(", outputWindow=").append(outputWindow) .append(", localEndStream=").append(localEndStream) .append(", idle=").append(idle); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append("["); appendState(buf); buf.append("]"); return buf.toString(); } } static class H2Stream { private final H2StreamChannelImpl channel; private final H2StreamHandler handler; private final boolean remoteInitiated; private H2Stream( final H2StreamChannelImpl channel, final H2StreamHandler handler, final boolean remoteInitiated) { this.channel = channel; this.handler = handler; this.remoteInitiated = remoteInitiated; } int getId() { return channel.getId(); } boolean isRemoteInitiated() { return remoteInitiated; } AtomicInteger getOutputWindow() { return channel.getOutputWindow(); } AtomicInteger getInputWindow() { return channel.getInputWindow(); } boolean isTerminated() { return channel.isLocalClosed() && (channel.isRemoteClosed() || channel.isResetDeadline()); } boolean isRemoteClosed() { return channel.isRemoteClosed(); } boolean isLocalClosed() { return channel.isLocalClosed(); } boolean isLocalReset() { return channel.isLocalReset(); } void setRemoteEndStream() { channel.setRemoteEndStream(); } void consumePromise(final List
headers) throws HttpException, IOException { try { handler.consumePromise(headers); channel.setLocalEndStream(); } catch (final ProtocolException ex) { localReset(ex, H2Error.PROTOCOL_ERROR); } } void consumeHeader(final List
headers) throws HttpException, IOException { try { handler.consumeHeader(headers, channel.isRemoteClosed()); } catch (final ProtocolException ex) { localReset(ex, H2Error.PROTOCOL_ERROR); } } void consumeData(final ByteBuffer src) throws HttpException, IOException { try { handler.consumeData(src, channel.isRemoteClosed()); } catch (final CharacterCodingException ex) { localReset(ex, H2Error.INTERNAL_ERROR); } catch (final ProtocolException ex) { localReset(ex, H2Error.PROTOCOL_ERROR); } } boolean isOutputReady() { return handler.isOutputReady(); } void produceOutput() throws HttpException, IOException { try { handler.produceOutput(); } catch (final ProtocolException ex) { localReset(ex, H2Error.PROTOCOL_ERROR); } } void produceInputCapacityUpdate() throws IOException { handler.updateInputCapacity(); } void reset(final Exception cause) { channel.setRemoteEndStream(); channel.setLocalEndStream(); handler.failed(cause); } void localReset(final Exception cause, final int code) throws IOException { channel.localReset(code); handler.failed(cause); } void localReset(final Exception cause, final H2Error error) throws IOException { localReset(cause, error != null ? error.getCode() : H2Error.INTERNAL_ERROR.getCode()); } void localReset(final H2StreamResetException ex) throws IOException { localReset(ex, ex.getCode()); } void handle(final HttpException ex) throws IOException, HttpException { handler.handle(ex, channel.isRemoteClosed()); } HandlerFactory getPushHandlerFactory() { return handler.getPushHandlerFactory(); } void cancel() { reset(new RequestNotExecutedException()); } boolean abort() { final boolean cancelled = channel.cancel(); handler.failed(new RequestNotExecutedException()); return cancelled; } void releaseResources() { handler.releaseResources(); } void appendState(final StringBuilder buf) { buf.append("channel=["); channel.appendState(buf); buf.append("]"); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append("["); appendState(buf); buf.append("]"); return buf.toString(); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/NoopH2StreamHandler.java0100664 0000000 0000000 00000005132 14245617503 027327 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; final class NoopH2StreamHandler implements H2StreamHandler { static final NoopH2StreamHandler INSTANCE = new NoopH2StreamHandler(); @Override public boolean isOutputReady() { return false; } @Override public void produceOutput() throws HttpException, IOException { } @Override public void consumePromise(final List
headers) throws HttpException, IOException { } @Override public void consumeHeader(final List
headers, final boolean endStream) throws HttpException, IOException { } @Override public void updateInputCapacity() throws IOException { } @Override public void consumeData(final ByteBuffer src, final boolean endStream) throws HttpException, IOException { } @Override public HandlerFactory getPushHandlerFactory() { return null; } @Override public void failed(final Exception cause) { } @Override public void handle(final HttpException ex, final boolean endStream) throws HttpException, IOException { } @Override public void releaseResources() { } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/0040775 0000000 0000000 00000000000 14437151052 024717 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java0100664 0000000 0000000 00000043436 14403631147 031130 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio.bootstrap; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.config.NamedElementChain; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.impl.bootstrap.StandardFilter; import org.apache.hc.core5.http.impl.nio.DefaultHttpRequestParserFactory; import org.apache.hc.core5.http.impl.nio.DefaultHttpResponseWriterFactory; import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory; import org.apache.hc.core5.http.nio.AsyncFilterHandler; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.nio.support.AsyncServerExpectationFilter; import org.apache.hc.core5.http.nio.support.AsyncServerFilterChainElement; import org.apache.hc.core5.http.nio.support.AsyncServerFilterChainExchangeHandlerFactory; import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator; import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler; import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory; import org.apache.hc.core5.http.nio.support.TerminalAsyncServerFilter; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.LookupRegistry; import org.apache.hc.core5.http.protocol.RequestHandlerRegistry; import org.apache.hc.core5.http.protocol.UriPatternType; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.impl.H2Processors; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.impl.nio.ServerH2StreamMultiplexerFactory; import org.apache.hc.core5.http2.impl.nio.ServerHttpProtocolNegotiationStarter; import org.apache.hc.core5.http2.ssl.H2ServerTlsStrategy; import org.apache.hc.core5.net.InetAddressUtils; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * HTTP/2 capable {@link HttpAsyncServer} bootstrap. * * @since 5.0 */ public class H2ServerBootstrap { private final List>> handlerList; private final List> filters; private String canonicalHostName; private LookupRegistry> lookupRegistry; private IOReactorConfig ioReactorConfig; private HttpProcessor httpProcessor; private CharCodingConfig charCodingConfig; private HttpVersionPolicy versionPolicy; private H2Config h2Config; private Http1Config http1Config; private TlsStrategy tlsStrategy; private Timeout handshakeTimeout; private Decorator ioSessionDecorator; private Callback exceptionCallback; private IOSessionListener sessionListener; private H2StreamListener h2StreamListener; private Http1StreamListener http1StreamListener; private H2ServerBootstrap() { this.handlerList = new ArrayList<>(); this.filters = new ArrayList<>(); } public static H2ServerBootstrap bootstrap() { return new H2ServerBootstrap(); } /** * Sets canonical name (fully qualified domain name) of the server. * * @since 5.0 */ public final H2ServerBootstrap setCanonicalHostName(final String canonicalHostName) { this.canonicalHostName = canonicalHostName; return this; } /** * Sets I/O reactor configuration. */ public final H2ServerBootstrap setIOReactorConfig(final IOReactorConfig ioReactorConfig) { this.ioReactorConfig = ioReactorConfig; return this; } /** * Assigns {@link HttpProcessor} instance. */ public final H2ServerBootstrap setHttpProcessor(final HttpProcessor httpProcessor) { this.httpProcessor = httpProcessor; return this; } /** * Sets HTTP protocol version policy */ public final H2ServerBootstrap setVersionPolicy(final HttpVersionPolicy versionPolicy) { this.versionPolicy = versionPolicy; return this; } /** * Sets HTTP/2 protocol parameters */ public final H2ServerBootstrap setH2Config(final H2Config h2Config) { this.h2Config = h2Config; return this; } /** * Sets HTTP/1.1 protocol parameters */ public final H2ServerBootstrap setHttp1Config(final Http1Config http1Config) { this.http1Config = http1Config; return this; } /** * Sets message char coding. */ public final H2ServerBootstrap setCharset(final CharCodingConfig charCodingConfig) { this.charCodingConfig = charCodingConfig; return this; } /** * Assigns {@link TlsStrategy} instance. */ public final H2ServerBootstrap setTlsStrategy(final TlsStrategy tlsStrategy) { this.tlsStrategy = tlsStrategy; return this; } public final H2ServerBootstrap setHandshakeTimeout(final Timeout handshakeTimeout) { this.handshakeTimeout = handshakeTimeout; return this; } /** * Assigns {@link IOSession} {@link Decorator} instance. */ public final H2ServerBootstrap setIOSessionDecorator(final Decorator ioSessionDecorator) { this.ioSessionDecorator = ioSessionDecorator; return this; } /** * Assigns {@link Exception} {@link Callback} instance. */ public final H2ServerBootstrap setExceptionCallback(final Callback exceptionCallback) { this.exceptionCallback = exceptionCallback; return this; } /** * Assigns {@link IOSessionListener} instance. */ public final H2ServerBootstrap setIOSessionListener(final IOSessionListener sessionListener) { this.sessionListener = sessionListener; return this; } /** * Assigns {@link H2StreamListener} instance. */ public final H2ServerBootstrap setStreamListener(final H2StreamListener h2StreamListener) { this.h2StreamListener = h2StreamListener; return this; } /** * Assigns {@link Http1StreamListener} instance. */ public final H2ServerBootstrap setStreamListener(final Http1StreamListener http1StreamListener) { this.http1StreamListener = http1StreamListener; return this; } /** * Assigns {@link LookupRegistry} instance. */ public final H2ServerBootstrap setLookupRegistry(final LookupRegistry> lookupRegistry) { this.lookupRegistry = lookupRegistry; return this; } /** * Registers the given {@link AsyncServerExchangeHandler} {@link Supplier} as a default handler for URIs * matching the given pattern. * * @param uriPattern the pattern to register the handler for. * @param supplier the handler supplier. */ public final H2ServerBootstrap register(final String uriPattern, final Supplier supplier) { Args.notBlank(uriPattern, "URI pattern"); Args.notNull(supplier, "Supplier"); handlerList.add(new HandlerEntry<>(null, uriPattern, supplier)); return this; } /** * Registers the given {@link AsyncServerExchangeHandler} {@link Supplier} as a handler for URIs * matching the given host and the pattern. * * @param hostname the host name * @param uriPattern the pattern to register the handler for. * @param supplier the handler supplier. */ public final H2ServerBootstrap registerVirtual(final String hostname, final String uriPattern, final Supplier supplier) { Args.notBlank(hostname, "Hostname"); Args.notBlank(uriPattern, "URI pattern"); Args.notNull(supplier, "Supplier"); handlerList.add(new HandlerEntry<>(hostname, uriPattern, supplier)); return this; } /** * Registers the given {@link AsyncServerRequestHandler} as a default handler for URIs * matching the given pattern. * * @param uriPattern the pattern to register the handler for. * @param requestHandler the handler. */ public final H2ServerBootstrap register( final String uriPattern, final AsyncServerRequestHandler requestHandler) { register(uriPattern, () -> new BasicServerExchangeHandler<>(requestHandler)); return this; } /** * Registers the given {@link AsyncServerRequestHandler} as a handler for URIs * matching the given host and the pattern. * * @param hostname the host name * @param uriPattern the pattern to register the handler for. * @param requestHandler the handler. */ public final H2ServerBootstrap registerVirtual( final String hostname, final String uriPattern, final AsyncServerRequestHandler requestHandler) { registerVirtual(hostname, uriPattern, () -> new BasicServerExchangeHandler<>(requestHandler)); return this; } /** * Adds the filter before the filter with the given name. */ public final H2ServerBootstrap addFilterBefore(final String existing, final String name, final AsyncFilterHandler filterHandler) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.BEFORE, name, filterHandler, existing)); return this; } /** * Adds the filter after the filter with the given name. */ public final H2ServerBootstrap addFilterAfter(final String existing, final String name, final AsyncFilterHandler filterHandler) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.AFTER, name, filterHandler, existing)); return this; } /** * Replace an existing filter with the given name with new filter. */ public final H2ServerBootstrap replaceFilter(final String existing, final AsyncFilterHandler filterHandler) { Args.notBlank(existing, "Existing"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.REPLACE, existing, filterHandler, existing)); return this; } /** * Add an filter to the head of the processing list. */ public final H2ServerBootstrap addFilterFirst(final String name, final AsyncFilterHandler filterHandler) { Args.notNull(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.FIRST, name, filterHandler, null)); return this; } /** * Add an filter to the tail of the processing list. */ public final H2ServerBootstrap addFilterLast(final String name, final AsyncFilterHandler filterHandler) { Args.notNull(name, "Name"); Args.notNull(filterHandler, "Filter handler"); filters.add(new FilterEntry<>(FilterEntry.Position.LAST, name, filterHandler, null)); return this; } public HttpAsyncServer create() { final String actualCanonicalHostName = canonicalHostName != null ? canonicalHostName : InetAddressUtils.getCanonicalLocalHostName(); final RequestHandlerRegistry> registry = new RequestHandlerRegistry<>( actualCanonicalHostName, () -> lookupRegistry != null ? lookupRegistry : UriPatternType.newMatcher(UriPatternType.URI_PATTERN)); for (final HandlerEntry> entry: handlerList) { registry.register(entry.hostname, entry.uriPattern, entry.handler); } final HandlerFactory handlerFactory; if (!filters.isEmpty()) { final NamedElementChain filterChainDefinition = new NamedElementChain<>(); filterChainDefinition.addLast( new TerminalAsyncServerFilter(new DefaultAsyncResponseExchangeHandlerFactory(registry)), StandardFilter.MAIN_HANDLER.name()); filterChainDefinition.addFirst( new AsyncServerExpectationFilter(), StandardFilter.EXPECT_CONTINUE.name()); for (final FilterEntry entry: filters) { switch (entry.position) { case AFTER: filterChainDefinition.addAfter(entry.existing, entry.filterHandler, entry.name); break; case BEFORE: filterChainDefinition.addBefore(entry.existing, entry.filterHandler, entry.name); break; case REPLACE: filterChainDefinition.replace(entry.existing, entry.filterHandler); break; case FIRST: filterChainDefinition.addFirst(entry.filterHandler, entry.name); break; case LAST: // Don't add last, after TerminalAsyncServerFilter, as that does not delegate to the chain // Instead, add the filter just before it, making it effectively the last filter filterChainDefinition.addBefore(StandardFilter.MAIN_HANDLER.name(), entry.filterHandler, entry.name); break; } } NamedElementChain.Node current = filterChainDefinition.getLast(); AsyncServerFilterChainElement execChain = null; while (current != null) { execChain = new AsyncServerFilterChainElement(current.getValue(), execChain); current = current.getPrevious(); } handlerFactory = new AsyncServerFilterChainExchangeHandlerFactory(execChain, exceptionCallback); } else { handlerFactory = new DefaultAsyncResponseExchangeHandlerFactory(registry, handler -> new BasicAsyncServerExpectationDecorator(handler, exceptionCallback)); } final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory = new ServerH2StreamMultiplexerFactory( httpProcessor != null ? httpProcessor : H2Processors.server(), handlerFactory, h2Config != null ? h2Config : H2Config.DEFAULT, charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT, h2StreamListener); final TlsStrategy actualTlsStrategy = tlsStrategy != null ? tlsStrategy : new H2ServerTlsStrategy(); final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ServerHttp1StreamDuplexerFactory( httpProcessor != null ? httpProcessor : HttpProcessors.server(), handlerFactory, http1Config != null ? http1Config : Http1Config.DEFAULT, charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT, DefaultConnectionReuseStrategy.INSTANCE, DefaultHttpRequestParserFactory.INSTANCE, DefaultHttpResponseWriterFactory.INSTANCE, DefaultContentLengthStrategy.INSTANCE, DefaultContentLengthStrategy.INSTANCE, http1StreamListener); final IOEventHandlerFactory ioEventHandlerFactory = new ServerHttpProtocolNegotiationStarter( http1StreamHandlerFactory, http2StreamHandlerFactory, versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE, actualTlsStrategy, handshakeTimeout); return new HttpAsyncServer(ioEventHandlerFactory, ioReactorConfig, ioSessionDecorator, exceptionCallback, sessionListener, actualCanonicalHostName); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/package-info.java0100664 0000000 0000000 00000002421 14245617503 030107 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP/2 capable requester and server bootstrap. */ package org.apache.hc.core5.http2.impl.nio.bootstrap; httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2AsyncRequester.java0100664 0000000 0000000 00000013576 14403631147 030743 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio.bootstrap; import java.util.concurrent.Future; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.ssl.ApplicationProtocol; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.pool.ManagedConnPool; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.util.Timeout; /** * Client side message exchange initiator capable of negotiating * HTTP/2 or HTTP/1.1 compatible connections. * * @since 5.0 */ public class H2AsyncRequester extends HttpAsyncRequester { private final HttpVersionPolicy versionPolicy; /** * Use {@link H2RequesterBootstrap} to create instances of this class. */ @Internal public H2AsyncRequester( final HttpVersionPolicy versionPolicy, final IOReactorConfig ioReactorConfig, final IOEventHandlerFactory eventHandlerFactory, final Decorator ioSessionDecorator, final Callback exceptionCallback, final IOSessionListener sessionListener, final ManagedConnPool connPool) { super(ioReactorConfig, eventHandlerFactory, ioSessionDecorator, exceptionCallback, sessionListener, connPool); this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE; } /** * Use {@link H2RequesterBootstrap} to create instances of this class. * * @since 5.2 */ @Internal public H2AsyncRequester( final HttpVersionPolicy versionPolicy, final IOReactorConfig ioReactorConfig, final IOEventHandlerFactory eventHandlerFactory, final Decorator ioSessionDecorator, final Callback exceptionCallback, final IOSessionListener sessionListener, final ManagedConnPool connPool, final TlsStrategy tlsStrategy, final Timeout handshakeTimeout) { super(ioReactorConfig, eventHandlerFactory, ioSessionDecorator, exceptionCallback, sessionListener, connPool, tlsStrategy, handshakeTimeout); this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE; } @Override protected Future doConnect( final HttpHost host, final Timeout timeout, final Object attachment, final FutureCallback callback) { return super.doConnect(host, timeout, attachment != null ? attachment : versionPolicy, callback); } @Override protected void doTlsUpgrade(final ProtocolIOSession ioSession, final NamedEndpoint endpoint, final FutureCallback callback) { super.doTlsUpgrade(ioSession, endpoint, new CallbackContribution(callback) { @Override public void completed(final ProtocolIOSession protocolSession) { final boolean switchProtocol; switch (versionPolicy) { case FORCE_HTTP_2: switchProtocol = true; break; case NEGOTIATE: final TlsDetails tlsDetails = protocolSession.getTlsDetails(); final String appProtocol = tlsDetails != null ? tlsDetails.getApplicationProtocol() : null; switchProtocol = ApplicationProtocol.HTTP_2.id.equals(appProtocol); break; default: switchProtocol = false; } if (switchProtocol) { protocolSession.switchProtocol(ApplicationProtocol.HTTP_2.id, callback); } else { if (callback != null) { callback.completed(protocolSession); } } } }); } } ././@LongLink0100644 0000000 0000000 00000000156 14403631147 011640 Lustar 0000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2MultiplexingRequesterBootstrap.javahttpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2MultiplexingRequesterBoots0100664 0000000 0000000 00000020640 14403631147 032424 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio.bootstrap; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.impl.DefaultAddressResolver; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestHandlerRegistry; import org.apache.hc.core5.http.protocol.UriPatternType; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.impl.H2Processors; import org.apache.hc.core5.http2.impl.nio.ClientH2StreamMultiplexerFactory; import org.apache.hc.core5.http2.impl.nio.ClientH2PrefaceHandler; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.nio.support.DefaultAsyncPushConsumerFactory; import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.util.Args; /** * {@link H2MultiplexingRequester} bootstrap. * * @since 5.0 */ public class H2MultiplexingRequesterBootstrap { private final List>> pushConsumerList; private UriPatternType uriPatternType; private IOReactorConfig ioReactorConfig; private HttpProcessor httpProcessor; private CharCodingConfig charCodingConfig; private H2Config h2Config; private TlsStrategy tlsStrategy; private boolean strictALPNHandshake; private Decorator ioSessionDecorator; private Callback exceptionCallback; private IOSessionListener sessionListener; private H2StreamListener streamListener; private H2MultiplexingRequesterBootstrap() { this.pushConsumerList = new ArrayList<>(); } public static H2MultiplexingRequesterBootstrap bootstrap() { return new H2MultiplexingRequesterBootstrap(); } /** * Sets I/O reactor configuration. */ public final H2MultiplexingRequesterBootstrap setIOReactorConfig(final IOReactorConfig ioReactorConfig) { this.ioReactorConfig = ioReactorConfig; return this; } /** * Assigns {@link HttpProcessor} instance. */ public final H2MultiplexingRequesterBootstrap setHttpProcessor(final HttpProcessor httpProcessor) { this.httpProcessor = httpProcessor; return this; } /** * Sets HTTP/2 protocol parameters */ public final H2MultiplexingRequesterBootstrap setH2Config(final H2Config h2Config) { this.h2Config = h2Config; return this; } /** * Sets message char coding. */ public final H2MultiplexingRequesterBootstrap setCharCodingConfig(final CharCodingConfig charCodingConfig) { this.charCodingConfig = charCodingConfig; return this; } /** * Assigns {@link TlsStrategy} instance. */ public final H2MultiplexingRequesterBootstrap setTlsStrategy(final TlsStrategy tlsStrategy) { this.tlsStrategy = tlsStrategy; return this; } public final H2MultiplexingRequesterBootstrap setStrictALPNHandshake(final boolean strictALPNHandshake) { this.strictALPNHandshake = strictALPNHandshake; return this; } /** * Assigns {@link IOSession} {@link Decorator} instance. */ public final H2MultiplexingRequesterBootstrap setIOSessionDecorator(final Decorator ioSessionDecorator) { this.ioSessionDecorator = ioSessionDecorator; return this; } /** * Assigns {@link Exception} {@link Callback} instance. */ public final H2MultiplexingRequesterBootstrap setExceptionCallback(final Callback exceptionCallback) { this.exceptionCallback = exceptionCallback; return this; } /** * Assigns {@link IOSessionListener} instance. */ public final H2MultiplexingRequesterBootstrap setIOSessionListener(final IOSessionListener sessionListener) { this.sessionListener = sessionListener; return this; } /** * Assigns {@link H2StreamListener} instance. */ public final H2MultiplexingRequesterBootstrap setStreamListener(final H2StreamListener streamListener) { this.streamListener = streamListener; return this; } /** * Assigns {@link UriPatternType} for handler registration. */ public final H2MultiplexingRequesterBootstrap setUriPatternType(final UriPatternType uriPatternType) { this.uriPatternType = uriPatternType; return this; } /** * Registers the given {@link AsyncPushConsumer} {@link Supplier} as a default handler for URIs * matching the given pattern. * * @param uriPattern the pattern to register the handler for. * @param supplier the handler supplier. */ public final H2MultiplexingRequesterBootstrap register(final String uriPattern, final Supplier supplier) { Args.notBlank(uriPattern, "URI pattern"); Args.notNull(supplier, "Supplier"); pushConsumerList.add(new HandlerEntry<>(null, uriPattern, supplier)); return this; } /** * Registers the given {@link AsyncPushConsumer} {@link Supplier} as a handler for URIs * matching the given host and the pattern. * * @param hostname the host name * @param uriPattern the pattern to register the handler for. * @param supplier the handler supplier. */ public final H2MultiplexingRequesterBootstrap registerVirtual(final String hostname, final String uriPattern, final Supplier supplier) { Args.notBlank(hostname, "Hostname"); Args.notBlank(uriPattern, "URI pattern"); Args.notNull(supplier, "Supplier"); pushConsumerList.add(new HandlerEntry<>(hostname, uriPattern, supplier)); return this; } public H2MultiplexingRequester create() { final RequestHandlerRegistry> registry = new RequestHandlerRegistry<>(uriPatternType); for (final HandlerEntry> entry: pushConsumerList) { registry.register(entry.hostname, entry.uriPattern, entry.handler); } final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory = new ClientH2StreamMultiplexerFactory( httpProcessor != null ? httpProcessor : H2Processors.client(), new DefaultAsyncPushConsumerFactory(registry), h2Config != null ? h2Config : H2Config.DEFAULT, charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT, streamListener); return new H2MultiplexingRequester( ioReactorConfig, (ioSession, attachment) -> new ClientH2PrefaceHandler(ioSession, http2StreamHandlerFactory, strictALPNHandshake), ioSessionDecorator, exceptionCallback, sessionListener, DefaultAddressResolver.INSTANCE, tlsStrategy != null ? tlsStrategy : new H2ClientTlsStrategy()); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java0100664 0000000 0000000 00000033037 14403631147 031635 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio.bootstrap; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory; import org.apache.hc.core5.http.impl.nio.DefaultHttpRequestWriterFactory; import org.apache.hc.core5.http.impl.nio.DefaultHttpResponseParserFactory; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestHandlerRegistry; import org.apache.hc.core5.http.protocol.UriPatternType; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.impl.H2Processors; import org.apache.hc.core5.http2.impl.nio.ClientH2StreamMultiplexerFactory; import org.apache.hc.core5.http2.impl.nio.ClientHttpProtocolNegotiationStarter; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.nio.support.DefaultAsyncPushConsumerFactory; import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy; import org.apache.hc.core5.pool.ConnPoolListener; import org.apache.hc.core5.pool.DefaultDisposalCallback; import org.apache.hc.core5.pool.LaxConnPool; import org.apache.hc.core5.pool.ManagedConnPool; import org.apache.hc.core5.pool.PoolConcurrencyPolicy; import org.apache.hc.core5.pool.PoolReusePolicy; import org.apache.hc.core5.pool.StrictConnPool; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * {@link H2AsyncRequester} bootstrap. * * @since 5.0 */ public class H2RequesterBootstrap { private final List>> pushConsumerList; private UriPatternType uriPatternType; private IOReactorConfig ioReactorConfig; private HttpProcessor httpProcessor; private CharCodingConfig charCodingConfig; private HttpVersionPolicy versionPolicy; private H2Config h2Config; private Http1Config http1Config; private int defaultMaxPerRoute; private int maxTotal; private TimeValue timeToLive; private PoolReusePolicy poolReusePolicy; private PoolConcurrencyPolicy poolConcurrencyPolicy; private TlsStrategy tlsStrategy; private Timeout handshakeTimeout; private Decorator ioSessionDecorator; private Callback exceptionCallback; private IOSessionListener sessionListener; private H2StreamListener streamListener; private Http1StreamListener http1StreamListener; private ConnPoolListener connPoolListener; private H2RequesterBootstrap() { this.pushConsumerList = new ArrayList<>(); } public static H2RequesterBootstrap bootstrap() { return new H2RequesterBootstrap(); } /** * Sets I/O reactor configuration. */ public final H2RequesterBootstrap setIOReactorConfig(final IOReactorConfig ioReactorConfig) { this.ioReactorConfig = ioReactorConfig; return this; } /** * Assigns {@link HttpProcessor} instance. */ public final H2RequesterBootstrap setHttpProcessor(final HttpProcessor httpProcessor) { this.httpProcessor = httpProcessor; return this; } /** * Sets HTTP protocol version policy */ public final H2RequesterBootstrap setVersionPolicy(final HttpVersionPolicy versionPolicy) { this.versionPolicy = versionPolicy; return this; } /** * Sets HTTP/2 protocol parameters */ public final H2RequesterBootstrap setH2Config(final H2Config h2Config) { this.h2Config = h2Config; return this; } /** * Sets HTTP/1.1 protocol parameters */ public final H2RequesterBootstrap setHttp1Config(final Http1Config http1Config) { this.http1Config = http1Config; return this; } /** * Sets message char coding. */ public final H2RequesterBootstrap setCharCodingConfig(final CharCodingConfig charCodingConfig) { this.charCodingConfig = charCodingConfig; return this; } public final H2RequesterBootstrap setDefaultMaxPerRoute(final int defaultMaxPerRoute) { this.defaultMaxPerRoute = defaultMaxPerRoute; return this; } public final H2RequesterBootstrap setMaxTotal(final int maxTotal) { this.maxTotal = maxTotal; return this; } public final H2RequesterBootstrap setTimeToLive(final TimeValue timeToLive) { this.timeToLive = timeToLive; return this; } /** * Assigns {@link PoolReusePolicy} instance. */ public final H2RequesterBootstrap setPoolReusePolicy(final PoolReusePolicy poolReusePolicy) { this.poolReusePolicy = poolReusePolicy; return this; } /** * Assigns {@link PoolConcurrencyPolicy} instance. */ @Experimental public final H2RequesterBootstrap setPoolConcurrencyPolicy(final PoolConcurrencyPolicy poolConcurrencyPolicy) { this.poolConcurrencyPolicy = poolConcurrencyPolicy; return this; } /** * Assigns {@link TlsStrategy} instance. */ public final H2RequesterBootstrap setTlsStrategy(final TlsStrategy tlsStrategy) { this.tlsStrategy = tlsStrategy; return this; } public final H2RequesterBootstrap setHandshakeTimeout(final Timeout handshakeTimeout) { this.handshakeTimeout = handshakeTimeout; return this; } /** * Assigns {@link IOSession} {@link Decorator} instance. */ public final H2RequesterBootstrap setIOSessionDecorator(final Decorator ioSessionDecorator) { this.ioSessionDecorator = ioSessionDecorator; return this; } /** * Assigns {@link Exception} {@link Callback} instance. */ public final H2RequesterBootstrap setExceptionCallback(final Callback exceptionCallback) { this.exceptionCallback = exceptionCallback; return this; } /** * Assigns {@link IOSessionListener} instance. */ public final H2RequesterBootstrap setIOSessionListener(final IOSessionListener sessionListener) { this.sessionListener = sessionListener; return this; } /** * Assigns {@link H2StreamListener} instance. */ public final H2RequesterBootstrap setStreamListener(final H2StreamListener streamListener) { this.streamListener = streamListener; return this; } /** * Assigns {@link Http1StreamListener} instance. */ public final H2RequesterBootstrap setStreamListener(final Http1StreamListener http1StreamListener) { this.http1StreamListener = http1StreamListener; return this; } /** * Assigns {@link ConnPoolListener} instance. */ public final H2RequesterBootstrap setConnPoolListener(final ConnPoolListener connPoolListener) { this.connPoolListener = connPoolListener; return this; } /** * Assigns {@link UriPatternType} for handler registration. */ public final H2RequesterBootstrap setUriPatternType(final UriPatternType uriPatternType) { this.uriPatternType = uriPatternType; return this; } /** * Registers the given {@link AsyncPushConsumer} {@link Supplier} as a default handler for URIs * matching the given pattern. * * @param uriPattern the pattern to register the handler for. * @param supplier the handler supplier. */ public final H2RequesterBootstrap register(final String uriPattern, final Supplier supplier) { Args.notBlank(uriPattern, "URI pattern"); Args.notNull(supplier, "Supplier"); pushConsumerList.add(new HandlerEntry<>(null, uriPattern, supplier)); return this; } /** * Registers the given {@link AsyncPushConsumer} {@link Supplier} as a handler for URIs * matching the given host and the pattern. * * @param hostname the host name * @param uriPattern the pattern to register the handler for. * @param supplier the handler supplier. */ public final H2RequesterBootstrap registerVirtual(final String hostname, final String uriPattern, final Supplier supplier) { Args.notBlank(hostname, "Hostname"); Args.notBlank(uriPattern, "URI pattern"); Args.notNull(supplier, "Supplier"); pushConsumerList.add(new HandlerEntry<>(hostname, uriPattern, supplier)); return this; } public H2AsyncRequester create() { final ManagedConnPool connPool; switch (poolConcurrencyPolicy != null ? poolConcurrencyPolicy : PoolConcurrencyPolicy.STRICT) { case LAX: connPool = new LaxConnPool<>( defaultMaxPerRoute > 0 ? defaultMaxPerRoute : 20, timeToLive, poolReusePolicy, new DefaultDisposalCallback<>(), connPoolListener); break; case STRICT: default: connPool = new StrictConnPool<>( defaultMaxPerRoute > 0 ? defaultMaxPerRoute : 20, maxTotal > 0 ? maxTotal : 50, timeToLive, poolReusePolicy, new DefaultDisposalCallback<>(), connPoolListener); break; } final RequestHandlerRegistry> registry = new RequestHandlerRegistry<>(uriPatternType); for (final HandlerEntry> entry: pushConsumerList) { registry.register(entry.hostname, entry.uriPattern, entry.handler); } final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory = new ClientH2StreamMultiplexerFactory( httpProcessor != null ? httpProcessor : H2Processors.client(), new DefaultAsyncPushConsumerFactory(registry), h2Config != null ? h2Config : H2Config.DEFAULT, charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT, streamListener); final TlsStrategy actualTlsStrategy = tlsStrategy != null ? tlsStrategy : new H2ClientTlsStrategy(); final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ClientHttp1StreamDuplexerFactory( httpProcessor != null ? httpProcessor : HttpProcessors.client(), http1Config != null ? http1Config : Http1Config.DEFAULT, charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT, DefaultConnectionReuseStrategy.INSTANCE, new DefaultHttpResponseParserFactory(http1Config), DefaultHttpRequestWriterFactory.INSTANCE, DefaultContentLengthStrategy.INSTANCE, DefaultContentLengthStrategy.INSTANCE, http1StreamListener); final IOEventHandlerFactory ioEventHandlerFactory = new ClientHttpProtocolNegotiationStarter( http1StreamHandlerFactory, http2StreamHandlerFactory, versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE, actualTlsStrategy, handshakeTimeout); return new H2AsyncRequester( versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE, ioReactorConfig, ioEventHandlerFactory, ioSessionDecorator, exceptionCallback, sessionListener, connPool, actualTlsStrategy, handshakeTimeout); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/CancellableExecution.java0100664 0000000 0000000 00000004720 14245617503 031640 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio.bootstrap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.CancellableDependency; final class CancellableExecution implements CancellableDependency { private final AtomicBoolean cancelled; private final AtomicReference dependencyRef; CancellableExecution() { this.cancelled = new AtomicBoolean(false); this.dependencyRef = new AtomicReference<>(); } @Override public void setDependency(final Cancellable cancellable) { dependencyRef.set(cancellable); if (cancelled.get()) { final Cancellable dependency = dependencyRef.getAndSet(null); if (dependency != null) { dependency.cancel(); } } } @Override public boolean isCancelled() { return cancelled.get(); } @Override public boolean cancel() { if (cancelled.compareAndSet(false, true)) { final Cancellable dependency = dependencyRef.getAndSet(null); if (dependency != null) { dependency.cancel(); } return true; } return false; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/HandlerEntry.java0100664 0000000 0000000 00000003650 14245617503 030167 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio.bootstrap; final class HandlerEntry { final String hostname; final String uriPattern; final T handler; public HandlerEntry(final String hostname, final String uriPattern, final T handler) { this.hostname = hostname; this.uriPattern = uriPattern; this.handler = handler; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("HandlerEntry [hostname="); builder.append(hostname); builder.append(", uriPattern="); builder.append(uriPattern); builder.append(", handler="); builder.append(handler); builder.append("]"); return builder.toString(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/FilterEntry.java0100664 0000000 0000000 00000003304 14403631147 030027 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio.bootstrap; class FilterEntry { enum Position {BEFORE, AFTER, REPLACE, FIRST, LAST} final Position position; final String name; final T filterHandler; final String existing; FilterEntry( final Position position, final String name, final T filterHandler, final String existing) { this.position = position; this.name = name; this.filterHandler = filterHandler; this.existing = existing; } } ././@LongLink0100644 0000000 0000000 00000000145 14437151052 011635 Lustar 0000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2MultiplexingRequester.javahttpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2MultiplexingRequester.java0100664 0000000 0000000 00000030476 14437151052 032344 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio.bootstrap; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.List; import java.util.Set; import java.util.concurrent.Future; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.concurrent.FutureContribution; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.impl.DefaultAddressResolver; import org.apache.hc.core5.http.impl.bootstrap.AsyncRequester; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.nio.command.RequestExecutionCommand; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.nio.support.BasicClientExchangeHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http2.nio.pool.H2ConnPool; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * HTTP/2 multiplexing client side message exchange initiator. * * @since 5.0 */ public class H2MultiplexingRequester extends AsyncRequester{ private final H2ConnPool connPool; /** * Use {@link H2MultiplexingRequesterBootstrap} to create instances of this class. */ @Internal public H2MultiplexingRequester( final IOReactorConfig ioReactorConfig, final IOEventHandlerFactory eventHandlerFactory, final Decorator ioSessionDecorator, final Callback exceptionCallback, final IOSessionListener sessionListener, final Resolver addressResolver, final TlsStrategy tlsStrategy) { super(eventHandlerFactory, ioReactorConfig, ioSessionDecorator, exceptionCallback, sessionListener, ShutdownCommand.GRACEFUL_IMMEDIATE_CALLBACK, DefaultAddressResolver.INSTANCE); this.connPool = new H2ConnPool(this, addressResolver, tlsStrategy); } public void closeIdle(final TimeValue idleTime) { connPool.closeIdle(idleTime); } public Set getRoutes() { return connPool.getRoutes(); } public TimeValue getValidateAfterInactivity() { return connPool.getValidateAfterInactivity(); } public void setValidateAfterInactivity(final TimeValue timeValue) { connPool.setValidateAfterInactivity(timeValue); } public Cancellable execute( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final Timeout timeout, final HttpContext context) { Args.notNull(exchangeHandler, "Exchange handler"); Args.notNull(timeout, "Timeout"); Args.notNull(context, "Context"); final CancellableExecution cancellableExecution = new CancellableExecution(); execute(exchangeHandler, pushHandlerFactory, cancellableExecution, timeout, context); return cancellableExecution; } public Cancellable execute( final AsyncClientExchangeHandler exchangeHandler, final Timeout timeout, final HttpContext context) { return execute(exchangeHandler, null, timeout, context); } private void execute( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final CancellableDependency cancellableDependency, final Timeout timeout, final HttpContext context) { Args.notNull(exchangeHandler, "Exchange handler"); Args.notNull(timeout, "Timeout"); Args.notNull(context, "Context"); try { exchangeHandler.produceRequest((request, entityDetails, httpContext) -> { final String scheme = request.getScheme(); final URIAuthority authority = request.getAuthority(); if (authority == null) { throw new ProtocolException("Request authority not specified"); } final HttpHost target = new HttpHost(scheme, authority); connPool.getSession(target, timeout, new FutureCallback() { @Override public void completed(final IOSession ioSession) { ioSession.enqueue(new RequestExecutionCommand(new AsyncClientExchangeHandler() { @Override public void releaseResources() { exchangeHandler.releaseResources(); } @Override public void produceRequest(final RequestChannel channel, final HttpContext httpContext) throws HttpException, IOException { channel.sendRequest(request, entityDetails, httpContext); } @Override public int available() { return exchangeHandler.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { exchangeHandler.produce(channel); } @Override public void consumeInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { exchangeHandler.consumeInformation(response, httpContext); } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext httpContext) throws HttpException, IOException { exchangeHandler.consumeResponse(response, entityDetails, httpContext); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { exchangeHandler.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { exchangeHandler.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { exchangeHandler.streamEnd(trailers); } @Override public void cancel() { exchangeHandler.cancel(); } @Override public void failed(final Exception cause) { exchangeHandler.failed(cause); } }, pushHandlerFactory, cancellableDependency, context), Command.Priority.NORMAL); if (!ioSession.isOpen()) { exchangeHandler.failed(new ConnectionClosedException()); } } @Override public void failed(final Exception ex) { exchangeHandler.failed(ex); } @Override public void cancelled() { exchangeHandler.cancel(); } }); }, context); } catch (final IOException | HttpException ex) { exchangeHandler.failed(ex); } } public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HandlerFactory pushHandlerFactory, final Timeout timeout, final HttpContext context, final FutureCallback callback) { Args.notNull(requestProducer, "Request producer"); Args.notNull(responseConsumer, "Response consumer"); Args.notNull(timeout, "Timeout"); final ComplexFuture future = new ComplexFuture<>(callback); final AsyncClientExchangeHandler exchangeHandler = new BasicClientExchangeHandler<>( requestProducer, responseConsumer, new FutureContribution(future) { @Override public void completed(final T result) { future.completed(result); } }); execute(exchangeHandler, pushHandlerFactory, future, timeout, context != null ? context : HttpCoreContext.create()); return future; } public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final Timeout timeout, final HttpContext context, final FutureCallback callback) { return execute(requestProducer, responseConsumer, null, timeout, context, callback); } public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final Timeout timeout, final FutureCallback callback) { return execute(requestProducer, responseConsumer, null, timeout, null, callback); } @Internal public H2ConnPool getConnPool() { return connPool; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2StreamHandler.java0100664 0000000 0000000 00000025077 14403631147 027640 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.impl.IncomingEntityDetails; import org.apache.hc.core5.http.impl.nio.MessageState; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.impl.DefaultH2RequestConverter; import org.apache.hc.core5.http2.impl.DefaultH2ResponseConverter; class ClientH2StreamHandler implements H2StreamHandler { private final H2StreamChannel outputChannel; private final DataStreamChannel dataChannel; private final HttpProcessor httpProcessor; private final BasicHttpConnectionMetrics connMetrics; private final AsyncClientExchangeHandler exchangeHandler; private final HandlerFactory pushHandlerFactory; private final HttpCoreContext context; private final AtomicBoolean requestCommitted; private final AtomicBoolean failed; private final AtomicBoolean done; private volatile MessageState requestState; private volatile MessageState responseState; ClientH2StreamHandler( final H2StreamChannel outputChannel, final HttpProcessor httpProcessor, final BasicHttpConnectionMetrics connMetrics, final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final HttpCoreContext context) { this.outputChannel = outputChannel; this.dataChannel = new DataStreamChannel() { @Override public void requestOutput() { outputChannel.requestOutput(); } @Override public int write(final ByteBuffer src) throws IOException { return outputChannel.write(src); } @Override public void endStream(final List trailers) throws IOException { outputChannel.endStream(trailers); requestState = MessageState.COMPLETE; } @Override public void endStream() throws IOException { outputChannel.endStream(); requestState = MessageState.COMPLETE; } }; this.httpProcessor = httpProcessor; this.connMetrics = connMetrics; this.exchangeHandler = exchangeHandler; this.pushHandlerFactory = pushHandlerFactory; this.context = context; this.requestCommitted = new AtomicBoolean(false); this.failed = new AtomicBoolean(false); this.done = new AtomicBoolean(false); this.requestState = MessageState.HEADERS; this.responseState = MessageState.HEADERS; } @Override public HandlerFactory getPushHandlerFactory() { return pushHandlerFactory; } @Override public boolean isOutputReady() { switch (requestState) { case HEADERS: return true; case BODY: return exchangeHandler.available() > 0; default: return false; } } private void commitRequest(final HttpRequest request, final EntityDetails entityDetails) throws HttpException, IOException { if (requestCommitted.compareAndSet(false, true)) { context.setProtocolVersion(HttpVersion.HTTP_2); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); httpProcessor.process(request, entityDetails, context); final List
headers = DefaultH2RequestConverter.INSTANCE.convert(request); outputChannel.submit(headers, entityDetails == null); connMetrics.incrementRequestCount(); if (entityDetails == null) { requestState = MessageState.COMPLETE; } else { final Header h = request.getFirstHeader(HttpHeaders.EXPECT); final boolean expectContinue = h != null && HeaderElements.CONTINUE.equalsIgnoreCase(h.getValue()); if (expectContinue) { requestState = MessageState.ACK; } else { requestState = MessageState.BODY; exchangeHandler.produce(dataChannel); } } } else { throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Request already committed"); } } @Override public void produceOutput() throws HttpException, IOException { switch (requestState) { case HEADERS: exchangeHandler.produceRequest((request, entityDetails, httpContext) -> commitRequest(request, entityDetails), context); break; case BODY: exchangeHandler.produce(dataChannel); break; } } @Override public void consumePromise(final List
headers) throws HttpException, IOException { throw new ProtocolException("Unexpected message promise"); } @Override public void consumeHeader(final List
headers, final boolean endStream) throws HttpException, IOException { if (done.get()) { throw new ProtocolException("Unexpected message headers"); } switch (responseState) { case HEADERS: final HttpResponse response = DefaultH2ResponseConverter.INSTANCE.convert(headers); final int status = response.getCode(); if (status < HttpStatus.SC_INFORMATIONAL) { throw new ProtocolException("Invalid response: " + new StatusLine(response)); } if (status > HttpStatus.SC_CONTINUE && status < HttpStatus.SC_SUCCESS) { exchangeHandler.consumeInformation(response, context); } if (requestState == MessageState.ACK) { if (status == HttpStatus.SC_CONTINUE || status >= HttpStatus.SC_SUCCESS) { requestState = MessageState.BODY; exchangeHandler.produce(dataChannel); } } if (status < HttpStatus.SC_SUCCESS) { return; } final EntityDetails entityDetails = endStream ? null : new IncomingEntityDetails(response, -1); context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); httpProcessor.process(response, entityDetails, context); connMetrics.incrementResponseCount(); exchangeHandler.consumeResponse(response, entityDetails, context); responseState = endStream ? MessageState.COMPLETE : MessageState.BODY; break; case BODY: responseState = MessageState.COMPLETE; exchangeHandler.streamEnd(headers); break; default: throw new ProtocolException("Unexpected message headers"); } } @Override public void updateInputCapacity() throws IOException { exchangeHandler.updateCapacity(outputChannel); } @Override public void consumeData(final ByteBuffer src, final boolean endStream) throws HttpException, IOException { if (done.get() || responseState != MessageState.BODY) { throw new ProtocolException("Unexpected message data"); } if (src != null) { exchangeHandler.consume(src); } if (endStream) { responseState = MessageState.COMPLETE; exchangeHandler.streamEnd(null); } } @Override public void handle(final HttpException ex, final boolean endStream) throws HttpException, IOException { throw ex; } @Override public void failed(final Exception cause) { try { if (failed.compareAndSet(false, true)) { if (exchangeHandler != null) { exchangeHandler.failed(cause); } } } finally { releaseResources(); } } @Override public void releaseResources() { if (done.compareAndSet(false, true)) { responseState = MessageState.COMPLETE; requestState = MessageState.COMPLETE; exchangeHandler.releaseResources(); } } @Override public String toString() { return "[" + "requestState=" + requestState + ", responseState=" + responseState + ']'; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2StreamMultiplexer.java0100664 0000000 0000000 00000013302 14245617503 030615 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.RequestHeaderFieldsTooLargeException; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.command.ExecutableCommand; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.DefaultFrameFactory; import org.apache.hc.core5.http2.frame.FrameFactory; import org.apache.hc.core5.http2.frame.StreamIdGenerator; import org.apache.hc.core5.http2.hpack.HeaderListConstraintException; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; /** * I/O event handler for events fired by {@link ProtocolIOSession} that implements * server side HTTP/2 messaging protocol with full support for * multiplexed message transmission. * * @since 5.0 */ @Internal public class ServerH2StreamMultiplexer extends AbstractH2StreamMultiplexer { private final HandlerFactory exchangeHandlerFactory; public ServerH2StreamMultiplexer( final ProtocolIOSession ioSession, final FrameFactory frameFactory, final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final CharCodingConfig charCodingConfig, final H2Config h2Config, final H2StreamListener streamListener) { super(ioSession, frameFactory, StreamIdGenerator.EVEN, httpProcessor, charCodingConfig, h2Config, streamListener); this.exchangeHandlerFactory = Args.notNull(exchangeHandlerFactory, "Handler factory"); } public ServerH2StreamMultiplexer( final ProtocolIOSession ioSession, final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final CharCodingConfig charCodingConfig, final H2Config h2Config) { this(ioSession, DefaultFrameFactory.INSTANCE, httpProcessor, exchangeHandlerFactory, charCodingConfig, h2Config, null); } @Override void acceptHeaderFrame() throws H2ConnectionException { } @Override void acceptPushRequest() throws H2ConnectionException { } @Override void acceptPushFrame() throws H2ConnectionException { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Push not supported"); } @Override H2StreamHandler createRemotelyInitiatedStream( final H2StreamChannel channel, final HttpProcessor httpProcessor, final BasicHttpConnectionMetrics connMetrics, final HandlerFactory pushHandlerFactory) throws IOException { final HttpCoreContext context = HttpCoreContext.create(); context.setAttribute(HttpCoreContext.SSL_SESSION, getSSLSession()); context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, getEndpointDetails()); return new ServerH2StreamHandler(channel, httpProcessor, connMetrics, exchangeHandlerFactory, context); } @Override H2StreamHandler createLocallyInitiatedStream( final ExecutableCommand command, final H2StreamChannel channel, final HttpProcessor httpProcessor, final BasicHttpConnectionMetrics connMetrics) throws IOException { throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Illegal attempt to execute a request"); } @Override List
decodeHeaders(final ByteBuffer payload) throws HttpException { try { return super.decodeHeaders(payload); } catch (final HeaderListConstraintException ex) { throw new RequestHeaderFieldsTooLargeException(ex.getMessage(), ex); } } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append("["); appendState(buf); buf.append("]"); return buf.toString(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2IOEventHandler.java0100664 0000000 0000000 00000004021 14245617503 027734 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import org.apache.hc.core5.net.InetAddressUtils; /** * {@link org.apache.hc.core5.reactor.IOEventHandler} that implements * server side HTTP/2 messaging protocol with full support for * multiplexed message transmission. * * @since 5.0 */ public class ServerH2IOEventHandler extends AbstractH2IOEventHandler { public ServerH2IOEventHandler(final ServerH2StreamMultiplexer streamMultiplexer) { super(streamMultiplexer); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); InetAddressUtils.formatAddress(buf, getRemoteAddress()); buf.append("->"); InetAddressUtils.formatAddress(buf, getLocalAddress()); buf.append(" ["); streamMultiplexer.appendState(buf); buf.append("]"); return buf.toString(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp1UpgradeHandler.java0100664 0000000 0000000 00000006311 14403631147 030541 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandler; import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ProtocolUpgradeHandler; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.util.Args; /** * Protocol upgrade handler that upgrades the underlying {@link ProtocolIOSession} * to HTTP/1.1 in case of a successful protocol negotiation or as a default fall-back. * * @since 5.2 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) @Internal public class ServerHttp1UpgradeHandler implements ProtocolUpgradeHandler { private final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory; public ServerHttp1UpgradeHandler(final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory) { this.http1StreamHandlerFactory = Args.notNull(http1StreamHandlerFactory, "HTTP/1.1 stream handler factory"); } @Override public void upgrade(final ProtocolIOSession ioSession, final FutureCallback callback) { final TlsDetails tlsDetails = ioSession.getTlsDetails(); final ServerHttp1IOEventHandler eventHandler = new ServerHttp1IOEventHandler(http1StreamHandlerFactory.create( tlsDetails != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id, ioSession)); ioSession.upgrade(eventHandler); ioSession.upgrade(eventHandler); try { eventHandler.connected(ioSession); if (callback != null) { callback.completed(ioSession); } } catch (final IOException ex) { eventHandler.exception(ioSession, ex); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/NoopAsyncPushHandler.java0100664 0000000 0000000 00000004775 14323605260 027625 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.protocol.HttpContext; class NoopAsyncPushHandler implements AsyncPushConsumer { @Override public void consumePromise(final HttpRequest promise, final HttpResponse response, final EntityDetails entityDetails, final HttpContext context) throws HttpException, IOException { } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(Integer.MAX_VALUE); } @Override public void consume(final ByteBuffer src) throws IOException { } @Override public void streamEnd(final List trailers) throws HttpException, IOException { } @Override public void failed(final Exception cause) { } @Override public void releaseResources() { } } ././@LongLink0100644 0000000 0000000 00000000150 14403631147 011632 Lustar 0000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiationStarter.javahttpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiationStarter.j0100664 0000000 0000000 00000011421 14403631147 032420 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler; import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory; import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.ssl.ApplicationProtocol; import org.apache.hc.core5.reactor.EndpointParameters; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * Client I/O event starter that prepares I/O sessions for an initial protocol handshake. * This class may return a different {@link org.apache.hc.core5.reactor.IOEventHandler} * implementation based on the current HTTP version policy. * * @since 5.1 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) @Internal public class ClientHttpProtocolNegotiationStarter implements IOEventHandlerFactory { private final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory; private final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory; private final HttpVersionPolicy versionPolicy; private final TlsStrategy tlsStrategy; private final Timeout handshakeTimeout; public ClientHttpProtocolNegotiationStarter( final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory, final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory, final HttpVersionPolicy versionPolicy, final TlsStrategy tlsStrategy, final Timeout handshakeTimeout) { this.http1StreamHandlerFactory = Args.notNull(http1StreamHandlerFactory, "HTTP/1.1 stream handler factory"); this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory"); this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE; this.tlsStrategy = tlsStrategy; this.handshakeTimeout = handshakeTimeout; } @Override public HttpConnectionEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { HttpVersionPolicy endpointPolicy = versionPolicy; if (attachment instanceof EndpointParameters) { final EndpointParameters params = (EndpointParameters) attachment; if (tlsStrategy != null && URIScheme.HTTPS.same(params.getScheme())) { tlsStrategy.upgrade(ioSession, params, params.getAttachment(), handshakeTimeout, null); } if (params.getAttachment() instanceof HttpVersionPolicy) { endpointPolicy = (HttpVersionPolicy) params.getAttachment(); } } ioSession.registerProtocol(ApplicationProtocol.HTTP_1_1.id, new ClientHttp1UpgradeHandler(http1StreamHandlerFactory)); ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ClientH2UpgradeHandler(http2StreamHandlerFactory)); switch (endpointPolicy) { case FORCE_HTTP_2: return new ClientH2PrefaceHandler(ioSession, http2StreamHandlerFactory, false); case FORCE_HTTP_1: return new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession)); default: return new HttpProtocolNegotiator(ioSession, null); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2StreamHandler.java0100664 0000000 0000000 00000034417 14245617503 027672 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.impl.IncomingEntityDetails; import org.apache.hc.core5.http.impl.ServerSupport; import org.apache.hc.core5.http.impl.nio.MessageState; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.AsyncResponseProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.nio.support.BasicResponseProducer; import org.apache.hc.core5.http.nio.support.ImmediateResponseExchangeHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.H2StreamResetException; import org.apache.hc.core5.http2.impl.DefaultH2RequestConverter; import org.apache.hc.core5.http2.impl.DefaultH2ResponseConverter; import org.apache.hc.core5.util.Asserts; class ServerH2StreamHandler implements H2StreamHandler { private final H2StreamChannel outputChannel; private final DataStreamChannel dataChannel; private final ResponseChannel responseChannel; private final HttpProcessor httpProcessor; private final BasicHttpConnectionMetrics connMetrics; private final HandlerFactory exchangeHandlerFactory; private final HttpCoreContext context; private final AtomicBoolean responseCommitted; private final AtomicBoolean failed; private final AtomicBoolean done; private volatile AsyncServerExchangeHandler exchangeHandler; private volatile HttpRequest receivedRequest; private volatile MessageState requestState; private volatile MessageState responseState; ServerH2StreamHandler( final H2StreamChannel outputChannel, final HttpProcessor httpProcessor, final BasicHttpConnectionMetrics connMetrics, final HandlerFactory exchangeHandlerFactory, final HttpCoreContext context) { this.outputChannel = outputChannel; this.dataChannel = new DataStreamChannel() { @Override public void requestOutput() { outputChannel.requestOutput(); } @Override public int write(final ByteBuffer src) throws IOException { return outputChannel.write(src); } @Override public void endStream(final List trailers) throws IOException { outputChannel.endStream(trailers); responseState = MessageState.COMPLETE; } @Override public void endStream() throws IOException { outputChannel.endStream(); responseState = MessageState.COMPLETE; } }; this.responseChannel = new ResponseChannel() { @Override public void sendInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { commitInformation(response); } @Override public void sendResponse( final HttpResponse response, final EntityDetails responseEntityDetails, final HttpContext httpContext) throws HttpException, IOException { ServerSupport.validateResponse(response, responseEntityDetails); commitResponse(response, responseEntityDetails); } @Override public void pushPromise( final HttpRequest promise, final AsyncPushProducer pushProducer, final HttpContext httpContext) throws HttpException, IOException { commitPromise(promise, pushProducer); } }; this.httpProcessor = httpProcessor; this.connMetrics = connMetrics; this.exchangeHandlerFactory = exchangeHandlerFactory; this.context = context; this.responseCommitted = new AtomicBoolean(false); this.failed = new AtomicBoolean(false); this.done = new AtomicBoolean(false); this.requestState = MessageState.HEADERS; this.responseState = MessageState.IDLE; } @Override public HandlerFactory getPushHandlerFactory() { return null; } private void commitInformation(final HttpResponse response) throws IOException, HttpException { if (responseCommitted.get()) { throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Response already committed"); } final int status = response.getCode(); if (status < HttpStatus.SC_INFORMATIONAL || status >= HttpStatus.SC_SUCCESS) { throw new HttpException("Invalid intermediate response: " + status); } final List
responseHeaders = DefaultH2ResponseConverter.INSTANCE.convert(response); outputChannel.submit(responseHeaders, false); } private void commitResponse( final HttpResponse response, final EntityDetails responseEntityDetails) throws HttpException, IOException { if (responseCommitted.compareAndSet(false, true)) { final int status = response.getCode(); if (status < HttpStatus.SC_SUCCESS) { throw new HttpException("Invalid response: " + status); } context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); httpProcessor.process(response, responseEntityDetails, context); final List
responseHeaders = DefaultH2ResponseConverter.INSTANCE.convert(response); final boolean endStream = responseEntityDetails == null || (receivedRequest != null && Method.HEAD.isSame(receivedRequest.getMethod())); outputChannel.submit(responseHeaders, endStream); connMetrics.incrementResponseCount(); if (responseEntityDetails == null) { responseState = MessageState.COMPLETE; } else { responseState = MessageState.BODY; exchangeHandler.produce(outputChannel); } } else { throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Response already committed"); } } private void commitPromise( final HttpRequest promise, final AsyncPushProducer pushProducer) throws HttpException, IOException { httpProcessor.process(promise, null, context); final List
promiseHeaders = DefaultH2RequestConverter.INSTANCE.convert(promise); outputChannel.push(promiseHeaders, pushProducer); connMetrics.incrementRequestCount(); } @Override public void consumePromise(final List
headers) throws HttpException, IOException { throw new ProtocolException("Unexpected message promise"); } @Override public void consumeHeader(final List
headers, final boolean endStream) throws HttpException, IOException { if (done.get()) { throw new ProtocolException("Unexpected message headers"); } switch (requestState) { case HEADERS: requestState = endStream ? MessageState.COMPLETE : MessageState.BODY; final HttpRequest request = DefaultH2RequestConverter.INSTANCE.convert(headers); final EntityDetails requestEntityDetails = endStream ? null : new IncomingEntityDetails(request, -1); final AsyncServerExchangeHandler handler; try { handler = exchangeHandlerFactory != null ? exchangeHandlerFactory.create(request, context) : null; } catch (final ProtocolException ex) { throw new H2StreamResetException(H2Error.PROTOCOL_ERROR, ex.getMessage()); } if (handler == null) { throw new H2StreamResetException(H2Error.REFUSED_STREAM, "Stream refused"); } exchangeHandler = handler; context.setProtocolVersion(HttpVersion.HTTP_2); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); try { httpProcessor.process(request, requestEntityDetails, context); connMetrics.incrementRequestCount(); receivedRequest = request; exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel, context); } catch (final HttpException ex) { if (!responseCommitted.get()) { final AsyncResponseProducer responseProducer = new BasicResponseProducer( ServerSupport.toStatusCode(ex), ServerSupport.toErrorMessage(ex)); exchangeHandler = new ImmediateResponseExchangeHandler(responseProducer); exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel, context); } else { throw ex; } } break; case BODY: responseState = MessageState.COMPLETE; exchangeHandler.streamEnd(headers); break; default: throw new ProtocolException("Unexpected message headers"); } } @Override public void updateInputCapacity() throws IOException { Asserts.notNull(exchangeHandler, "Exchange handler"); exchangeHandler.updateCapacity(outputChannel); } @Override public void consumeData(final ByteBuffer src, final boolean endStream) throws HttpException, IOException { if (done.get() || requestState != MessageState.BODY) { throw new ProtocolException("Unexpected message data"); } Asserts.notNull(exchangeHandler, "Exchange handler"); if (src != null) { exchangeHandler.consume(src); } if (endStream) { requestState = MessageState.COMPLETE; exchangeHandler.streamEnd(null); } } @Override public boolean isOutputReady() { return responseState == MessageState.BODY && exchangeHandler != null && exchangeHandler.available() > 0; } @Override public void produceOutput() throws HttpException, IOException { if (responseState == MessageState.BODY) { Asserts.notNull(exchangeHandler, "Exchange handler"); exchangeHandler.produce(dataChannel); } } @Override public void handle(final HttpException ex, final boolean endStream) throws HttpException, IOException { if (done.get()) { throw ex; } switch (requestState) { case HEADERS: requestState = endStream ? MessageState.COMPLETE : MessageState.BODY; if (!responseCommitted.get()) { final AsyncResponseProducer responseProducer = new BasicResponseProducer( ServerSupport.toStatusCode(ex), ServerSupport.toErrorMessage(ex)); exchangeHandler = new ImmediateResponseExchangeHandler(responseProducer); exchangeHandler.handleRequest(null, null, responseChannel, context); } else { throw ex; } break; case BODY: responseState = MessageState.COMPLETE; default: throw ex; } } @Override public void failed(final Exception cause) { try { if (failed.compareAndSet(false, true)) { if (exchangeHandler != null) { exchangeHandler.failed(cause); } } } finally { releaseResources(); } } @Override public void releaseResources() { if (done.compareAndSet(false, true)) { requestState = MessageState.COMPLETE; responseState = MessageState.COMPLETE; if (exchangeHandler != null) { exchangeHandler.releaseResources(); } } } @Override public String toString() { return "[" + "requestState=" + requestState + ", responseState=" + responseState + ']'; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/FrameOutputBuffer.java0100664 0000000 0000000 00000011325 14442305435 027153 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.GatheringByteChannel; import java.nio.channels.WritableByteChannel; import org.apache.hc.core5.http2.H2TransportMetrics; import org.apache.hc.core5.http2.frame.FrameConsts; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics; import org.apache.hc.core5.util.Args; /** * Frame output buffer for HTTP/2 non-blocking connections. * * @since 5.0 */ public final class FrameOutputBuffer { private final BasicH2TransportMetrics metrics; private int maxFramePayloadSize; private ByteBuffer buffer; public FrameOutputBuffer(final BasicH2TransportMetrics metrics, final int maxFramePayloadSize) { Args.notNull(metrics, "HTTP2 transport metrics"); Args.positive(maxFramePayloadSize, "Maximum payload size"); this.metrics = metrics; this.maxFramePayloadSize = maxFramePayloadSize; this.buffer = ByteBuffer.allocate(FrameConsts.HEAD_LEN + maxFramePayloadSize); } public FrameOutputBuffer(final int maxFramePayloadSize) { this(new BasicH2TransportMetrics(), maxFramePayloadSize); } /** * @deprecated Misnomer. Use {@link #resize(int)}. */ @Deprecated public void expand(final int maxFramePayloadSize) { resize(maxFramePayloadSize); } /** * @since 5.2 */ public int getMaxFramePayloadSize() { return maxFramePayloadSize; } /** * @since 5.2 */ public void resize(final int maxFramePayloadSize) { if (buffer.capacity() == maxFramePayloadSize) { return; } this.maxFramePayloadSize = maxFramePayloadSize; final ByteBuffer newBuffer = ByteBuffer.allocate(FrameConsts.HEAD_LEN + maxFramePayloadSize); if (buffer.position() > 0) { buffer.flip(); newBuffer.put(buffer); } buffer = newBuffer; } public void write(final RawFrame frame, final WritableByteChannel channel) throws IOException { Args.notNull(frame, "Frame"); final ByteBuffer payload = frame.getPayload(); Args.check(payload == null || payload.remaining() <= maxFramePayloadSize, "Frame size exceeds maximum"); buffer.putInt((payload != null ? payload.remaining() << 8 : 0) | (frame.getType() & 0xff)); buffer.put((byte) (frame.getFlags() & 0xff)); buffer.putInt(frame.getStreamId()); if (payload != null) { if (channel instanceof GatheringByteChannel) { buffer.flip(); ((GatheringByteChannel) channel).write(new ByteBuffer[]{buffer, payload}); buffer.compact(); if (payload.hasRemaining()) { buffer.put(payload); } } else { buffer.put(payload); } } flush(channel); metrics.incrementFramesTransferred(); } public void flush(final WritableByteChannel channel) throws IOException { if (buffer.position() > 0) { buffer.flip(); try { final int bytesWritten = channel.write(buffer); if (bytesWritten > 0) { metrics.incrementBytesTransferred(bytesWritten); } } finally { buffer.compact(); } } } public boolean isEmpty() { return buffer.position() == 0; } public H2TransportMetrics getMetrics() { return metrics; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2StreamMultiplexerFactory.java0100664 0000000 0000000 00000007174 14245617503 032127 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.DefaultFrameFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; /** * {@link ClientH2StreamMultiplexer} factory. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) @Internal public final class ClientH2StreamMultiplexerFactory { private final HttpProcessor httpProcessor; private final HandlerFactory pushHandlerFactory; private final H2Config h2Config; private final CharCodingConfig charCodingConfig; private final H2StreamListener streamListener; public ClientH2StreamMultiplexerFactory( final HttpProcessor httpProcessor, final HandlerFactory pushHandlerFactory, final H2Config h2Config, final CharCodingConfig charCodingConfig, final H2StreamListener streamListener) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.pushHandlerFactory = pushHandlerFactory; this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.streamListener = streamListener; } public ClientH2StreamMultiplexerFactory( final HttpProcessor httpProcessor, final HandlerFactory pushHandlerFactory, final H2StreamListener streamListener) { this(httpProcessor, pushHandlerFactory, null, null, streamListener); } public ClientH2StreamMultiplexerFactory( final HttpProcessor httpProcessor, final H2StreamListener streamListener) { this(httpProcessor, null, streamListener); } public ClientH2StreamMultiplexer create(final ProtocolIOSession ioSession) { return new ClientH2StreamMultiplexer(ioSession, DefaultFrameFactory.INSTANCE, httpProcessor, pushHandlerFactory, h2Config, charCodingConfig, streamListener); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ProtocolNegotiationException.java0100664 0000000 0000000 00000003445 14403631147 031432 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; /** * Signals a protocol error in HTTP protocol negotiation. * * @since 5.1 */ public class ProtocolNegotiationException extends IOException { /** * Required for serialization support. * * @see java.io.Serializable */ private static final long serialVersionUID = 6211774735704945037L; /** * Creates a MessageConstraintException with the specified detail message. * * @param message The exception detail message */ public ProtocolNegotiationException(final String message) { super(message); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2StreamListener.java0100664 0000000 0000000 00000004323 14245617503 026704 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.util.List; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http2.frame.RawFrame; /** * HTTP/2 stream event listener. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public interface H2StreamListener { void onHeaderInput(HttpConnection connection, int streamId, List headers); void onHeaderOutput(HttpConnection connection, int streamId, List headers); void onFrameInput(HttpConnection connection, int streamId, RawFrame frame); void onFrameOutput(HttpConnection connection, int streamId, RawFrame frame); void onInputFlowControl(HttpConnection connection, int streamId, int delta, int actualSize); void onOutputFlowControl(HttpConnection connection, int streamId, int delta, int actualSize); } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerPushH2StreamHandler.java0100664 0000000 0000000 00000023350 14245617503 030524 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.impl.nio.MessageState; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.impl.DefaultH2RequestConverter; import org.apache.hc.core5.http2.impl.DefaultH2ResponseConverter; class ServerPushH2StreamHandler implements H2StreamHandler { private final H2StreamChannel outputChannel; private final DataStreamChannel dataChannel; private final HttpProcessor httpProcessor; private final BasicHttpConnectionMetrics connMetrics; private final AsyncPushProducer pushProducer; private final HttpCoreContext context; private final AtomicBoolean responseCommitted; private final AtomicBoolean failed; private final AtomicBoolean done; private volatile MessageState requestState; private volatile MessageState responseState; ServerPushH2StreamHandler( final H2StreamChannel outputChannel, final HttpProcessor httpProcessor, final BasicHttpConnectionMetrics connMetrics, final AsyncPushProducer pushProducer, final HttpCoreContext context) { this.outputChannel = outputChannel; this.dataChannel = new DataStreamChannel() { @Override public void requestOutput() { outputChannel.requestOutput(); } @Override public int write(final ByteBuffer src) throws IOException { return outputChannel.write(src); } @Override public void endStream(final List trailers) throws IOException { outputChannel.endStream(trailers); responseState = MessageState.COMPLETE; } @Override public void endStream() throws IOException { outputChannel.endStream(); responseState = MessageState.COMPLETE; } }; this.httpProcessor = httpProcessor; this.connMetrics = connMetrics; this.pushProducer = pushProducer; this.context = context; this.responseCommitted = new AtomicBoolean(false); this.failed = new AtomicBoolean(false); this.done = new AtomicBoolean(false); this.requestState = MessageState.COMPLETE; this.responseState = MessageState.IDLE; } @Override public HandlerFactory getPushHandlerFactory() { return null; } @Override public void consumePromise(final List
headers) throws HttpException, IOException { throw new ProtocolException("Unexpected message promise"); } @Override public void consumeHeader(final List
requestHeaders, final boolean requestEndStream) throws HttpException, IOException { throw new ProtocolException("Unexpected message headers"); } @Override public void updateInputCapacity() throws IOException { } @Override public void consumeData(final ByteBuffer src, final boolean endStream) throws HttpException, IOException { throw new ProtocolException("Unexpected message data"); } @Override public boolean isOutputReady() { switch (responseState) { case IDLE: return true; case BODY: return pushProducer.available() > 0; default: return false; } } private void commitInformation(final HttpResponse response) throws IOException, HttpException { if (responseCommitted.get()) { throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Response already committed"); } final int status = response.getCode(); if (status < HttpStatus.SC_INFORMATIONAL || status >= HttpStatus.SC_SUCCESS) { throw new HttpException("Invalid intermediate response: " + status); } final List
responseHeaders = DefaultH2ResponseConverter.INSTANCE.convert(response); outputChannel.submit(responseHeaders, false); } private void commitResponse( final HttpResponse response, final EntityDetails responseEntityDetails) throws HttpException, IOException { if (responseCommitted.compareAndSet(false, true)) { context.setProtocolVersion(HttpVersion.HTTP_2); context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); httpProcessor.process(response, responseEntityDetails, context); final List
headers = DefaultH2ResponseConverter.INSTANCE.convert(response); outputChannel.submit(headers, responseEntityDetails == null); connMetrics.incrementResponseCount(); if (responseEntityDetails == null) { responseState = MessageState.COMPLETE; } else { responseState = MessageState.BODY; pushProducer.produce(outputChannel); } } } private void commitPromise( final HttpRequest promise, final AsyncPushProducer pushProducer) throws HttpException, IOException { context.setProtocolVersion(HttpVersion.HTTP_2); context.setAttribute(HttpCoreContext.HTTP_REQUEST, promise); httpProcessor.process(promise, null, context); final List
headers = DefaultH2RequestConverter.INSTANCE.convert(promise); outputChannel.push(headers, pushProducer); connMetrics.incrementRequestCount(); } @Override public void produceOutput() throws HttpException, IOException { switch (responseState) { case IDLE: responseState = MessageState.HEADERS; pushProducer.produceResponse(new ResponseChannel() { @Override public void sendInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { commitInformation(response); } @Override public void sendResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext httpContext) throws HttpException, IOException { commitResponse(response, entityDetails); } @Override public void pushPromise( final HttpRequest promise, final AsyncPushProducer pushProducer, final HttpContext httpContext) throws HttpException, IOException { commitPromise(promise, pushProducer); } }, context); break; case BODY: pushProducer.produce(dataChannel); break; } } @Override public void handle(final HttpException ex, final boolean endStream) throws HttpException, IOException { throw ex; } @Override public void failed(final Exception cause) { try { if (failed.compareAndSet(false, true)) { pushProducer.failed(cause); } } finally { releaseResources(); } } @Override public void releaseResources() { if (done.compareAndSet(false, true)) { requestState = MessageState.COMPLETE; responseState = MessageState.COMPLETE; pushProducer.releaseResources(); } } @Override public String toString() { return "[" + "requestState=" + requestState + ", responseState=" + responseState + ']'; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2UpgradeHandler.java0100664 0000000 0000000 00000005404 14403631147 027764 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ProtocolUpgradeHandler; import org.apache.hc.core5.util.Args; /** * Protocol upgrade handler that upgrades the underlying {@link ProtocolIOSession} * to HTTP/2 in case of a successful protocol negotiation. * * @since 5.2 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) @Internal public class ClientH2UpgradeHandler implements ProtocolUpgradeHandler { private final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory; public ClientH2UpgradeHandler(final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory) { this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory"); } @Override public void upgrade(final ProtocolIOSession ioSession, final FutureCallback callback) { final HttpConnectionEventHandler protocolNegotiator = new ClientH2PrefaceHandler( ioSession, http2StreamHandlerFactory, true, callback); ioSession.upgrade(protocolNegotiator); try { protocolNegotiator.connected(ioSession); } catch (final IOException ex) { protocolNegotiator.exception(ioSession, ex); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2UpgradeHandler.java0100664 0000000 0000000 00000005376 14403631147 030024 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ProtocolUpgradeHandler; import org.apache.hc.core5.util.Args; /** * Protocol upgrade handler that upgrades the underlying {@link ProtocolIOSession} * to HTTP/2 in case of a successful protocol negotiation. * * @since 5.2 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) @Internal public class ServerH2UpgradeHandler implements ProtocolUpgradeHandler { private final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory; public ServerH2UpgradeHandler(final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory) { this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory"); } @Override public void upgrade(final ProtocolIOSession ioSession, final FutureCallback callback) { final HttpConnectionEventHandler protocolNegotiator = new ServerH2PrefaceHandler( ioSession, http2StreamHandlerFactory, callback); ioSession.upgrade(protocolNegotiator); try { protocolNegotiator.connected(ioSession); } catch (final IOException ex) { protocolNegotiator.exception(ioSession, ex); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp1UpgradeHandler.java0100664 0000000 0000000 00000005643 14403631147 030520 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler; import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ProtocolUpgradeHandler; import org.apache.hc.core5.util.Args; /** * Protocol upgrade handler that upgrades the underlying {@link ProtocolIOSession} * to HTTP/1.1 in case of a successful protocol negotiation or as a default fall-back. * * @since 5.2 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) @Internal public class ClientHttp1UpgradeHandler implements ProtocolUpgradeHandler { private final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory; public ClientHttp1UpgradeHandler(final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory) { this.http1StreamHandlerFactory = Args.notNull(http1StreamHandlerFactory, "HTTP/1.1 stream handler factory"); } @Override public void upgrade(final ProtocolIOSession ioSession, final FutureCallback callback) { final ClientHttp1IOEventHandler eventHandler = new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession)); ioSession.upgrade(eventHandler); try { eventHandler.connected(ioSession); if (callback != null) { callback.completed(ioSession); } } catch (final IOException ex) { eventHandler.exception(ioSession, ex); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2IOEventHandler.java0100664 0000000 0000000 00000004021 14245617503 027704 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import org.apache.hc.core5.net.InetAddressUtils; /** * {@link org.apache.hc.core5.reactor.IOEventHandler} that implements * client side HTTP/2 messaging protocol with full support for * multiplexed message transmission. * * @since 5.0 */ public class ClientH2IOEventHandler extends AbstractH2IOEventHandler { public ClientH2IOEventHandler(final ClientH2StreamMultiplexer streamMultiplexer) { super(streamMultiplexer); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); InetAddressUtils.formatAddress(buf, getLocalAddress()); buf.append("->"); InetAddressUtils.formatAddress(buf, getRemoteAddress()); buf.append(" ["); streamMultiplexer.appendState(buf); buf.append("]"); return buf.toString(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2StreamMultiplexerFactory.java0100664 0000000 0000000 00000006476 14245617503 032163 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.DefaultFrameFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; /** * {@link ServerH2StreamMultiplexer} factory. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) @Internal public final class ServerH2StreamMultiplexerFactory { private final HttpProcessor httpProcessor; private final HandlerFactory exchangeHandlerFactory; private final H2Config h2Config; private final CharCodingConfig charCodingConfig; private final H2StreamListener streamListener; public ServerH2StreamMultiplexerFactory( final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final H2Config h2Config, final CharCodingConfig charCodingConfig, final H2StreamListener streamListener) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.exchangeHandlerFactory = Args.notNull(exchangeHandlerFactory, "Exchange handler factory"); this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.streamListener = streamListener; } public ServerH2StreamMultiplexer create(final ProtocolIOSession ioSession) { return new ServerH2StreamMultiplexer( ioSession, DefaultFrameFactory.INSTANCE, httpProcessor, exchangeHandlerFactory, charCodingConfig, h2Config, streamListener); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2StreamChannel.java0100664 0000000 0000000 00000003514 14245617503 026470 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.util.List; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.nio.AsyncPushProducer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; interface H2StreamChannel extends DataStreamChannel, CapacityChannel, Cancellable { void submit(List
headers, boolean endStream) throws HttpException, IOException; void push(List
headers, AsyncPushProducer pushProducer) throws HttpException, IOException; } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/HttpProtocolNegotiator.java0100664 0000000 0000000 00000015140 14403631147 030241 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLSession; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler; import org.apache.hc.core5.http.nio.command.CommandSupport; import org.apache.hc.core5.http2.ssl.ApplicationProtocol; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.SocketTimeoutExceptionFactory; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Timeout; /** * @since 5.2 */ @Internal public class HttpProtocolNegotiator implements HttpConnectionEventHandler { private final ProtocolIOSession ioSession; private final FutureCallback resultCallback; private final AtomicBoolean completed; private final AtomicReference negotiatedProtocolRef; public HttpProtocolNegotiator( final ProtocolIOSession ioSession, final FutureCallback resultCallback) { this.ioSession = Args.notNull(ioSession, "I/O session"); this.resultCallback = resultCallback; this.completed = new AtomicBoolean(); this.negotiatedProtocolRef = new AtomicReference<>(); } void startProtocol(final HttpVersion httpVersion) { ioSession.switchProtocol( httpVersion == HttpVersion.HTTP_2 ? ApplicationProtocol.HTTP_2.id : ApplicationProtocol.HTTP_1_1.id, resultCallback); negotiatedProtocolRef.set(httpVersion); } @Override public void connected(final IOSession session) throws IOException { final HttpVersion httpVersion; final TlsDetails tlsDetails = ioSession.getTlsDetails(); if (tlsDetails != null) { final String appProtocol = tlsDetails.getApplicationProtocol(); if (TextUtils.isEmpty(appProtocol)) { httpVersion = HttpVersion.HTTP_1_1; } else if (appProtocol.equals(ApplicationProtocol.HTTP_1_1.id)) { httpVersion = HttpVersion.HTTP_1_1; } else if (appProtocol.equals(ApplicationProtocol.HTTP_2.id)) { httpVersion = HttpVersion.HTTP_2; } else { throw new ProtocolNegotiationException("Unsupported application protocol: " + appProtocol); } } else { httpVersion = HttpVersion.HTTP_1_1; } startProtocol(httpVersion); } @Override public void inputReady(final IOSession session, final ByteBuffer src) throws IOException { throw new ProtocolNegotiationException("Unexpected input"); } @Override public void outputReady(final IOSession session) throws IOException { throw new ProtocolNegotiationException("Unexpected output"); } @Override public void timeout(final IOSession session, final Timeout timeout) { exception(session, SocketTimeoutExceptionFactory.create(timeout)); } @Override public void exception(final IOSession session, final Exception cause) { try { session.close(CloseMode.IMMEDIATE); CommandSupport.failCommands(session, cause); } catch (final Exception ex) { if (completed.compareAndSet(false, true) && resultCallback != null) { resultCallback.failed(ex); } } } @Override public void disconnected(final IOSession session) { try { CommandSupport.cancelCommands(session); } finally { if (completed.compareAndSet(false, true) && resultCallback != null) { resultCallback.failed(new ConnectionClosedException()); } } } @Override public SSLSession getSSLSession() { final TlsDetails tlsDetails = ioSession.getTlsDetails(); return tlsDetails != null ? tlsDetails.getSSLSession() : null; } @Override public EndpointDetails getEndpointDetails() { return null; } @Override public void setSocketTimeout(final Timeout timeout) { ioSession.setSocketTimeout(timeout); } @Override public Timeout getSocketTimeout() { return ioSession.getSocketTimeout(); } @Override public ProtocolVersion getProtocolVersion() { return negotiatedProtocolRef.get(); } @Override public SocketAddress getRemoteAddress() { return ioSession.getRemoteAddress(); } @Override public SocketAddress getLocalAddress() { return ioSession.getLocalAddress(); } @Override public boolean isOpen() { return ioSession.isOpen(); } @Override public void close() throws IOException { ioSession.close(); } @Override public void close(final CloseMode closeMode) { ioSession.close(closeMode); } @Override public String toString() { return getClass().getName(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2StreamMultiplexer.java0100664 0000000 0000000 00000014624 14245617503 030575 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.command.ExecutableCommand; import org.apache.hc.core5.http.nio.command.RequestExecutionCommand; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.DefaultFrameFactory; import org.apache.hc.core5.http2.frame.FrameFactory; import org.apache.hc.core5.http2.frame.StreamIdGenerator; import org.apache.hc.core5.reactor.ProtocolIOSession; /** * I/O event handler for events fired by {@link ProtocolIOSession} that implements * client side HTTP/2 messaging protocol with full support for * multiplexed message transmission. * * @since 5.0 */ @Internal public class ClientH2StreamMultiplexer extends AbstractH2StreamMultiplexer { private final HandlerFactory pushHandlerFactory; public ClientH2StreamMultiplexer( final ProtocolIOSession ioSession, final FrameFactory frameFactory, final HttpProcessor httpProcessor, final HandlerFactory pushHandlerFactory, final H2Config h2Config, final CharCodingConfig charCodingConfig, final H2StreamListener streamListener) { super(ioSession, frameFactory, StreamIdGenerator.ODD, httpProcessor, charCodingConfig, h2Config, streamListener); this.pushHandlerFactory = pushHandlerFactory; } public ClientH2StreamMultiplexer( final ProtocolIOSession ioSession, final HttpProcessor httpProcessor, final HandlerFactory pushHandlerFactory, final H2Config h2Config, final CharCodingConfig charCodingConfig) { this(ioSession, DefaultFrameFactory.INSTANCE, httpProcessor, pushHandlerFactory, h2Config, charCodingConfig, null); } public ClientH2StreamMultiplexer( final ProtocolIOSession ioSession, final HttpProcessor httpProcessor, final H2Config h2Config, final CharCodingConfig charCodingConfig) { this(ioSession, httpProcessor, null, h2Config, charCodingConfig); } @Override void acceptHeaderFrame() throws H2ConnectionException { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal HEADERS frame"); } @Override void acceptPushFrame() throws H2ConnectionException { } @Override void acceptPushRequest() throws H2ConnectionException { throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Illegal attempt to push a response"); } @Override H2StreamHandler createLocallyInitiatedStream( final ExecutableCommand command, final H2StreamChannel channel, final HttpProcessor httpProcessor, final BasicHttpConnectionMetrics connMetrics) throws IOException { if (command instanceof RequestExecutionCommand) { final RequestExecutionCommand executionCommand = (RequestExecutionCommand) command; final AsyncClientExchangeHandler exchangeHandler = executionCommand.getExchangeHandler(); final HandlerFactory pushHandlerFactory = executionCommand.getPushHandlerFactory(); final HttpCoreContext context = HttpCoreContext.adapt(executionCommand.getContext()); context.setAttribute(HttpCoreContext.SSL_SESSION, getSSLSession()); context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, getEndpointDetails()); return new ClientH2StreamHandler(channel, httpProcessor, connMetrics, exchangeHandler, pushHandlerFactory != null ? pushHandlerFactory : this.pushHandlerFactory, context); } throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Unexpected executable command"); } @Override H2StreamHandler createRemotelyInitiatedStream( final H2StreamChannel channel, final HttpProcessor httpProcessor, final BasicHttpConnectionMetrics connMetrics, final HandlerFactory pushHandlerFactory) throws IOException { final HttpCoreContext context = HttpCoreContext.create(); context.setAttribute(HttpCoreContext.SSL_SESSION, getSSLSession()); context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, getEndpointDetails()); return new ClientPushH2StreamHandler(channel, httpProcessor, connMetrics, pushHandlerFactory != null ? pushHandlerFactory : this.pushHandlerFactory, context); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append("["); appendState(buf); buf.append("]"); return buf.toString(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2PrefaceHandler.java0100664 0000000 0000000 00000013543 14403631147 027745 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.impl.nio.BufferedData; import org.apache.hc.core5.http2.ssl.ApplicationProtocol; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * I/O event handler for events fired by {@link ProtocolIOSession} that implements * client side of the HTTP/2 protocol negotiation handshake always forcing the choice * of HTTP/2. * * @since 5.2 */ @Internal public class ClientH2PrefaceHandler extends PrefaceHandlerBase { // PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n final static byte[] PREFACE = new byte[] { 0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a}; private final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory; private final boolean strictALPNHandshake; private final AtomicBoolean initialized; private volatile ByteBuffer preface; private volatile BufferedData inBuf; public ClientH2PrefaceHandler( final ProtocolIOSession ioSession, final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory, final boolean strictALPNHandshake) { this(ioSession, http2StreamHandlerFactory, strictALPNHandshake, null); } /** * @since 5.1 */ public ClientH2PrefaceHandler( final ProtocolIOSession ioSession, final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory, final boolean strictALPNHandshake, final FutureCallback resultCallback) { super(ioSession, resultCallback); this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory"); this.strictALPNHandshake = strictALPNHandshake; this.initialized = new AtomicBoolean(); } private void initialize() throws IOException { final TlsDetails tlsDetails = ioSession.getTlsDetails(); if (tlsDetails != null) { final String applicationProtocol = tlsDetails.getApplicationProtocol(); if (TextUtils.isEmpty(applicationProtocol)) { if (strictALPNHandshake) { throw new ProtocolNegotiationException("ALPN: missing application protocol"); } } else { if (!ApplicationProtocol.HTTP_2.id.equals(applicationProtocol)) { throw new ProtocolNegotiationException("ALPN: unexpected application protocol '" + applicationProtocol + "'"); } } } this.preface = ByteBuffer.wrap(PREFACE); ioSession.setEvent(SelectionKey.OP_WRITE); } private void writeOutPreface(final IOSession session) throws IOException { if (preface.hasRemaining()) { session.write(preface); } if (!preface.hasRemaining()) { session.clearEvent(SelectionKey.OP_WRITE); final ByteBuffer data = inBuf != null ? inBuf.data() : null; startProtocol(new ClientH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), data); if (inBuf != null) { inBuf.clear(); } preface = null; } } @Override public void connected(final IOSession session) throws IOException { if (initialized.compareAndSet(false, true)) { initialize(); } } @Override public void outputReady(final IOSession session) throws IOException { if (initialized.compareAndSet(false, true)) { initialize(); } if (preface != null) { writeOutPreface(session); } else { throw new ProtocolNegotiationException("Unexpected output"); } } @Override public void inputReady(final IOSession session, final ByteBuffer src) throws IOException { if (src != null) { if (inBuf == null) { inBuf = BufferedData.allocate(src.remaining()); } inBuf.put(src); } if (preface != null) { writeOutPreface(session); } else { throw new ProtocolNegotiationException("Unexpected input"); } } @Override public String toString() { return getClass().getName(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushH2StreamHandler.java0100664 0000000 0000000 00000020002 14323605260 030455 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.impl.IncomingEntityDetails; import org.apache.hc.core5.http.impl.nio.MessageState; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.H2StreamResetException; import org.apache.hc.core5.http2.impl.DefaultH2RequestConverter; import org.apache.hc.core5.http2.impl.DefaultH2ResponseConverter; import org.apache.hc.core5.util.Asserts; class ClientPushH2StreamHandler implements H2StreamHandler { private final H2StreamChannel internalOutputChannel; private final HttpProcessor httpProcessor; private final BasicHttpConnectionMetrics connMetrics; private final HandlerFactory pushHandlerFactory; private final HttpCoreContext context; private final AtomicBoolean failed; private final AtomicBoolean done; private volatile HttpRequest request; private volatile AsyncPushConsumer exchangeHandler; private volatile MessageState requestState; private volatile MessageState responseState; ClientPushH2StreamHandler( final H2StreamChannel outputChannel, final HttpProcessor httpProcessor, final BasicHttpConnectionMetrics connMetrics, final HandlerFactory pushHandlerFactory, final HttpCoreContext context) { this.internalOutputChannel = outputChannel; this.httpProcessor = httpProcessor; this.connMetrics = connMetrics; this.pushHandlerFactory = pushHandlerFactory; this.context = context; this.failed = new AtomicBoolean(false); this.done = new AtomicBoolean(false); this.requestState = MessageState.HEADERS; this.responseState = MessageState.HEADERS; } @Override public HandlerFactory getPushHandlerFactory() { return pushHandlerFactory; } @Override public boolean isOutputReady() { return false; } @Override public void produceOutput() throws HttpException, IOException { } @Override public void consumePromise(final List
headers) throws HttpException, IOException { if (requestState == MessageState.HEADERS) { request = DefaultH2RequestConverter.INSTANCE.convert(headers); try { exchangeHandler = pushHandlerFactory != null ? pushHandlerFactory.create(request, context) : null; } catch (final ProtocolException ex) { exchangeHandler = new NoopAsyncPushHandler(); throw new H2StreamResetException(H2Error.PROTOCOL_ERROR, ex.getMessage()); } if (exchangeHandler == null) { exchangeHandler = new NoopAsyncPushHandler(); throw new H2StreamResetException(H2Error.REFUSED_STREAM, "Stream refused"); } context.setProtocolVersion(HttpVersion.HTTP_2); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); httpProcessor.process(request, null, context); connMetrics.incrementRequestCount(); this.requestState = MessageState.COMPLETE; } else { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected promise"); } } @Override public void consumeHeader(final List
headers, final boolean endStream) throws HttpException, IOException { if (responseState == MessageState.HEADERS) { Asserts.notNull(request, "Request"); Asserts.notNull(exchangeHandler, "Exchange handler"); final HttpResponse response = DefaultH2ResponseConverter.INSTANCE.convert(headers); final EntityDetails entityDetails = endStream ? null : new IncomingEntityDetails(request, -1); context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); httpProcessor.process(response, entityDetails, context); connMetrics.incrementResponseCount(); exchangeHandler.consumePromise(request, response, entityDetails, context); if (endStream) { responseState = MessageState.COMPLETE; exchangeHandler.streamEnd(null); } else { responseState = MessageState.BODY; } } else { throw new ProtocolException("Unexpected message headers"); } } @Override public void updateInputCapacity() throws IOException { Asserts.notNull(exchangeHandler, "Exchange handler"); exchangeHandler.updateCapacity(internalOutputChannel); } @Override public void consumeData(final ByteBuffer src, final boolean endStream) throws HttpException, IOException { if (responseState != MessageState.BODY) { throw new ProtocolException("Unexpected message data"); } Asserts.notNull(exchangeHandler, "Exchange handler"); if (src != null) { exchangeHandler.consume(src); } if (endStream) { responseState = MessageState.COMPLETE; exchangeHandler.streamEnd(null); } } public boolean isDone() { return responseState == MessageState.COMPLETE; } @Override public void failed(final Exception cause) { try { if (failed.compareAndSet(false, true)) { if (exchangeHandler != null) { exchangeHandler.failed(cause); } } } finally { releaseResources(); } } @Override public void handle(final HttpException ex, final boolean endStream) throws HttpException { throw ex; } @Override public void releaseResources() { if (done.compareAndSet(false, true)) { responseState = MessageState.COMPLETE; requestState = MessageState.COMPLETE; if (exchangeHandler != null) { exchangeHandler.releaseResources(); } } } @Override public String toString() { return "[" + "requestState=" + requestState + ", responseState=" + responseState + ']'; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/PrefaceHandlerBase.java0100664 0000000 0000000 00000013361 14403631147 027205 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLSession; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler; import org.apache.hc.core5.http.nio.command.CommandSupport; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.SocketTimeoutExceptionFactory; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; abstract class PrefaceHandlerBase implements HttpConnectionEventHandler { final ProtocolIOSession ioSession; private final AtomicReference protocolHandlerRef; private final FutureCallback resultCallback; private final AtomicBoolean completed; PrefaceHandlerBase( final ProtocolIOSession ioSession, final FutureCallback resultCallback) { this.ioSession = Args.notNull(ioSession, "I/O session"); this.protocolHandlerRef = new AtomicReference<>(); this.resultCallback = resultCallback; this.completed = new AtomicBoolean(); } void startProtocol(final HttpConnectionEventHandler protocolHandler, final ByteBuffer data) throws IOException { protocolHandlerRef.set(protocolHandler); ioSession.upgrade(protocolHandler); protocolHandler.connected(ioSession); if (data != null && data.hasRemaining()) { protocolHandler.inputReady(ioSession, data); } if (completed.compareAndSet(false, true) && resultCallback != null) { resultCallback.completed(ioSession); } } @Override public void timeout(final IOSession session, final Timeout timeout) { exception(session, SocketTimeoutExceptionFactory.create(timeout)); } @Override public void exception(final IOSession session, final Exception cause) { final HttpConnectionEventHandler protocolHandler = protocolHandlerRef.get(); try { session.close(CloseMode.IMMEDIATE); if (protocolHandler != null) { protocolHandler.exception(session, cause); } else { CommandSupport.failCommands(session, cause); } } catch (final Exception ex) { if (completed.compareAndSet(false, true) && resultCallback != null) { resultCallback.failed(ex); } } } @Override public void disconnected(final IOSession session) { final HttpConnectionEventHandler protocolHandler = protocolHandlerRef.getAndSet(null); try { if (protocolHandler != null) { protocolHandler.disconnected(ioSession); } else { CommandSupport.cancelCommands(session); } } finally { if (completed.compareAndSet(false, true) && resultCallback != null) { resultCallback.failed(new ConnectionClosedException()); } } } @Override public SSLSession getSSLSession() { final TlsDetails tlsDetails = ioSession.getTlsDetails(); return tlsDetails != null ? tlsDetails.getSSLSession() : null; } @Override public EndpointDetails getEndpointDetails() { return null; } @Override public void setSocketTimeout(final Timeout timeout) { ioSession.setSocketTimeout(timeout); } @Override public Timeout getSocketTimeout() { return ioSession.getSocketTimeout(); } @Override public ProtocolVersion getProtocolVersion() { return HttpVersion.HTTP_2; } @Override public SocketAddress getRemoteAddress() { return ioSession.getRemoteAddress(); } @Override public SocketAddress getLocalAddress() { return ioSession.getLocalAddress(); } @Override public boolean isOpen() { return ioSession.isOpen(); } @Override public void close() throws IOException { ioSession.close(); } @Override public void close(final CloseMode closeMode) { ioSession.close(closeMode); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2PrefaceHandler.java0100664 0000000 0000000 00000007666 14403631147 030006 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.impl.nio.BufferedData; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; /** * I/O event handler for events fired by {@link ProtocolIOSession} that implements * server side of the HTTP/2 protocol negotiation handshake. * * @since 5.2 */ @Internal public class ServerH2PrefaceHandler extends PrefaceHandlerBase { final static byte[] PREFACE = ClientH2PrefaceHandler.PREFACE; private final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory; private final BufferedData inBuf; public ServerH2PrefaceHandler( final ProtocolIOSession ioSession, final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory) { this(ioSession, http2StreamHandlerFactory, null); } public ServerH2PrefaceHandler( final ProtocolIOSession ioSession, final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory, final FutureCallback resultCallback) { super(ioSession, resultCallback); this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory"); this.inBuf = BufferedData.allocate(1024); } @Override public void connected(final IOSession session) throws IOException { } @Override public void inputReady(final IOSession session, final ByteBuffer src) throws IOException { if (src != null) { inBuf.put(src); } boolean endOfStream = false; if (inBuf.length() < PREFACE.length) { final int bytesRead = inBuf.readFrom(session); if (bytesRead == -1) { endOfStream = true; } } final ByteBuffer data = inBuf.data(); if (data.remaining() >= PREFACE.length) { for (int i = 0; i < PREFACE.length; i++) { if (data.get() != PREFACE[i]) { throw new ProtocolNegotiationException("Unexpected HTTP/2 preface"); } } startProtocol(new ServerH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), data.hasRemaining() ? data : null); } else { if (endOfStream) { throw new ConnectionClosedException(); } } } @Override public void outputReady(final IOSession session) throws IOException { } @Override public String toString() { return getClass().getName(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2StreamHandler.java0100664 0000000 0000000 00000004263 14245617503 026477 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.ResourceHolder; interface H2StreamHandler extends ResourceHolder { boolean isOutputReady(); void produceOutput() throws HttpException, IOException; void consumePromise(List
headers) throws HttpException, IOException; void consumeHeader(List
headers, boolean endStream) throws HttpException, IOException; void updateInputCapacity() throws IOException; void consumeData(ByteBuffer src, boolean endStream) throws HttpException, IOException; HandlerFactory getPushHandlerFactory(); void failed(Exception cause); void handle(final HttpException ex, final boolean endStream) throws HttpException, IOException; } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2IOEventHandler.java0100664 0000000 0000000 00000010645 14245617503 030242 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.net.SocketAddress; import java.nio.ByteBuffer; import javax.net.ssl.SSLSession; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; class AbstractH2IOEventHandler implements HttpConnectionEventHandler { final AbstractH2StreamMultiplexer streamMultiplexer; AbstractH2IOEventHandler(final AbstractH2StreamMultiplexer streamMultiplexer) { this.streamMultiplexer = Args.notNull(streamMultiplexer, "Stream multiplexer"); } @Override public void connected(final IOSession session) throws IOException { try { streamMultiplexer.onConnect(); } catch (final HttpException ex) { streamMultiplexer.onException(ex); } } @Override public void inputReady(final IOSession session, final ByteBuffer src) throws IOException { try { streamMultiplexer.onInput(src); } catch (final HttpException ex) { streamMultiplexer.onException(ex); } } @Override public void outputReady(final IOSession session) throws IOException { try { streamMultiplexer.onOutput(); } catch (final HttpException ex) { streamMultiplexer.onException(ex); } } @Override public void timeout(final IOSession session, final Timeout timeout) throws IOException { try { streamMultiplexer.onTimeout(timeout); } catch (final HttpException ex) { streamMultiplexer.onException(ex); } } @Override public void exception(final IOSession session, final Exception cause) { streamMultiplexer.onException(cause); } @Override public void disconnected(final IOSession session) { streamMultiplexer.onDisconnect(); } @Override public void close() throws IOException { streamMultiplexer.close(); } @Override public void close(final CloseMode closeMode) { streamMultiplexer.close(closeMode); } @Override public boolean isOpen() { return streamMultiplexer.isOpen(); } @Override public void setSocketTimeout(final Timeout timeout) { streamMultiplexer.setSocketTimeout(timeout); } @Override public SSLSession getSSLSession() { return streamMultiplexer.getSSLSession(); } @Override public EndpointDetails getEndpointDetails() { return streamMultiplexer.getEndpointDetails(); } @Override public Timeout getSocketTimeout() { return streamMultiplexer.getSocketTimeout(); } @Override public ProtocolVersion getProtocolVersion() { return streamMultiplexer.getProtocolVersion(); } @Override public SocketAddress getRemoteAddress() { return streamMultiplexer.getRemoteAddress(); } @Override public SocketAddress getLocalAddress() { return streamMultiplexer.getLocalAddress(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/FrameInputBuffer.java0100664 0000000 0000000 00000017454 14442015161 026755 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2CorruptFrameException; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.H2TransportMetrics; import org.apache.hc.core5.http2.frame.FrameConsts; import org.apache.hc.core5.http2.frame.FrameFlag; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics; import org.apache.hc.core5.util.Args; /** * Frame input buffer for HTTP/2 non-blocking connections. * * @since 5.0 */ public final class FrameInputBuffer { enum State { HEAD_EXPECTED, PAYLOAD_EXPECTED } private final BasicH2TransportMetrics metrics; private final int maxFramePayloadSize; private final byte[] bytes; private final ByteBuffer buffer; private State state; private int payloadLen; private int type; private int flags; private int streamId; FrameInputBuffer(final BasicH2TransportMetrics metrics, final int bufferLen, final int maxFramePayloadSize) { Args.notNull(metrics, "HTTP2 transport metrics"); Args.positive(maxFramePayloadSize, "Maximum payload size"); this.metrics = metrics; this.maxFramePayloadSize = Math.max(maxFramePayloadSize, FrameConsts.MIN_FRAME_SIZE); this.bytes = new byte[bufferLen]; this.buffer = ByteBuffer.wrap(bytes); this.buffer.flip(); this.state = State.HEAD_EXPECTED; } public FrameInputBuffer(final BasicH2TransportMetrics metrics, final int maxFramePayloadSize) { this(metrics, FrameConsts.HEAD_LEN + maxFramePayloadSize, maxFramePayloadSize); } public FrameInputBuffer(final int maxFramePayloadSize) { this(new BasicH2TransportMetrics(), maxFramePayloadSize); } /** * @deprecated Use {@link #read(ByteBuffer, ReadableByteChannel)}. */ @Deprecated public void put(final ByteBuffer src) { if (buffer.hasRemaining()) { buffer.compact(); } else { buffer.clear(); } buffer.put(src); buffer.flip(); } /** * Attempts to read a complete frame from the given source buffer and the underlying data * channel. The source buffer is consumed first. More data can be read from the channel * if required. * * @param src the source buffer or {@code null} if not available. * @param channel the underlying data channel. * * @return a complete frame or {@code null} a complete frame cannot be read. * * @since 5.1 */ public RawFrame read(final ByteBuffer src, final ReadableByteChannel channel) throws IOException { for (;;) { if (src != null) { if (buffer.hasRemaining()) { buffer.compact(); } else { buffer.clear(); } final int remaining = buffer.remaining(); final int n = src.remaining(); if (remaining >= n) { buffer.put(src); metrics.incrementBytesTransferred(n); } else { final int limit = src.limit(); src.limit(remaining); buffer.put(src); src.limit(limit); metrics.incrementBytesTransferred(remaining); } buffer.flip(); } switch (state) { case HEAD_EXPECTED: if (buffer.remaining() >= FrameConsts.HEAD_LEN) { final int lengthAndType = buffer.getInt(); payloadLen = lengthAndType >> 8; if (payloadLen > maxFramePayloadSize) { throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Frame size exceeds maximum"); } type = lengthAndType & 0xff; flags = buffer.get(); streamId = Math.abs(buffer.getInt()); state = State.PAYLOAD_EXPECTED; } else { break; } case PAYLOAD_EXPECTED: if (buffer.remaining() >= payloadLen) { if ((flags & FrameFlag.PADDED.getValue()) > 0) { if (payloadLen == 0) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Inconsistent padding"); } buffer.mark(); final int padding = buffer.get(); if (payloadLen < padding + 1) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Inconsistent padding"); } buffer.reset(); } final ByteBuffer payload = payloadLen > 0 ? ByteBuffer.wrap(bytes, buffer.position(), payloadLen) : null; buffer.position(buffer.position() + payloadLen); state = State.HEAD_EXPECTED; metrics.incrementFramesTransferred(); return new RawFrame(type, flags, streamId, payload); } } if (buffer.hasRemaining()) { buffer.compact(); } else { buffer.clear(); } final int bytesRead = channel.read(buffer); buffer.flip(); if (bytesRead > 0) { metrics.incrementBytesTransferred(bytesRead); } if (bytesRead == 0) { break; } else if (bytesRead < 0) { if (state != State.HEAD_EXPECTED || buffer.hasRemaining()) { throw new H2CorruptFrameException("Corrupt or incomplete HTTP2 frame"); } else { throw new ConnectionClosedException(); } } } return null; } /** * Attempts to read a complete frame from the underlying data channel. * * @param channel the underlying data channel. */ public RawFrame read(final ReadableByteChannel channel) throws IOException { return read(null, channel); } public void reset() { buffer.compact(); state = State.HEAD_EXPECTED; } public H2TransportMetrics getMetrics() { return metrics; } } ././@LongLink0100644 0000000 0000000 00000000150 14403631147 011632 Lustar 0000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiationStarter.javahttpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiationStarter.j0100664 0000000 0000000 00000011563 14403631147 032457 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler; import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandler; import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.ssl.ApplicationProtocol; import org.apache.hc.core5.reactor.EndpointParameters; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * Server I/O event starter that prepares I/O sessions for an initial protocol handshake. * This class may return a different {@link org.apache.hc.core5.reactor.IOEventHandler} * implementation based on the current HTTP version policy. * * @since 5.1 */ @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) @Internal public class ServerHttpProtocolNegotiationStarter implements IOEventHandlerFactory { private final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory; private final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory; private final HttpVersionPolicy versionPolicy; private final TlsStrategy tlsStrategy; private final Timeout handshakeTimeout; public ServerHttpProtocolNegotiationStarter( final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory, final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory, final HttpVersionPolicy versionPolicy, final TlsStrategy tlsStrategy, final Timeout handshakeTimeout) { this.http1StreamHandlerFactory = Args.notNull(http1StreamHandlerFactory, "HTTP/1.1 stream handler factory"); this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory"); this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE; this.tlsStrategy = tlsStrategy; this.handshakeTimeout = handshakeTimeout; } @Override public HttpConnectionEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { HttpVersionPolicy endpointPolicy = versionPolicy; URIScheme uriScheme = URIScheme.HTTP; if (attachment instanceof EndpointParameters) { final EndpointParameters params = (EndpointParameters) attachment; if (tlsStrategy != null && URIScheme.HTTPS.same(params.getScheme())) { uriScheme = URIScheme.HTTPS; tlsStrategy.upgrade(ioSession, params, params.getAttachment(), handshakeTimeout, null); } if (params.getAttachment() instanceof HttpVersionPolicy) { endpointPolicy = (HttpVersionPolicy) params.getAttachment(); } } ioSession.registerProtocol(ApplicationProtocol.HTTP_1_1.id, new ServerHttp1UpgradeHandler(http1StreamHandlerFactory)); ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ServerH2UpgradeHandler(http2StreamHandlerFactory)); switch (endpointPolicy) { case FORCE_HTTP_2: return new ServerH2PrefaceHandler(ioSession, http2StreamHandlerFactory); case FORCE_HTTP_1: return new ServerHttp1IOEventHandler(http1StreamHandlerFactory.create(uriScheme.id, ioSession)); default: return new HttpProtocolNegotiator(ioSession, null); } } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/DefaultH2RequestConverter.java0100664 0000000 0000000 00000023212 14403631147 027775 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http2.H2MessageConverter; import org.apache.hc.core5.http2.H2PseudoRequestHeaders; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.TextUtils; /** * HTTP/2 request converter. * * @since 5.0 */ public final class DefaultH2RequestConverter implements H2MessageConverter { public final static DefaultH2RequestConverter INSTANCE = new DefaultH2RequestConverter(); @Override public HttpRequest convert(final List
headers) throws HttpException { String method = null; String scheme = null; String authority = null; String path = null; final List
messageHeaders = new ArrayList<>(); for (int i = 0; i < headers.size(); i++) { final Header header = headers.get(i); final String name = header.getName(); final String value = header.getValue(); for (int n = 0; n < name.length(); n++) { final char ch = name.charAt(n); if (Character.isAlphabetic(ch) && !Character.isLowerCase(ch)) { throw new ProtocolException("Header name '%s' is invalid (header name contains uppercase characters)", name); } } if (name.startsWith(":")) { if (!messageHeaders.isEmpty()) { throw new ProtocolException("Invalid sequence of headers (pseudo-headers must precede message headers)"); } switch (name) { case H2PseudoRequestHeaders.METHOD: if (method != null) { throw new ProtocolException("Multiple '%s' request headers are illegal", name); } method = value; break; case H2PseudoRequestHeaders.SCHEME: if (scheme != null) { throw new ProtocolException("Multiple '%s' request headers are illegal", name); } scheme = value; break; case H2PseudoRequestHeaders.PATH: if (path != null) { throw new ProtocolException("Multiple '%s' request headers are illegal", name); } path = value; break; case H2PseudoRequestHeaders.AUTHORITY: authority = value; break; default: throw new ProtocolException("Unsupported request header '%s'", name); } } else { if (name.equalsIgnoreCase(HttpHeaders.CONNECTION) || name.equalsIgnoreCase(HttpHeaders.KEEP_ALIVE) || name.equalsIgnoreCase(HttpHeaders.PROXY_CONNECTION) || name.equalsIgnoreCase(HttpHeaders.TRANSFER_ENCODING) || name.equalsIgnoreCase(HttpHeaders.HOST) || name.equalsIgnoreCase(HttpHeaders.UPGRADE)) { throw new ProtocolException("Header '%s: %s' is illegal for HTTP/2 messages", header.getName(), header.getValue()); } if (name.equalsIgnoreCase(HttpHeaders.TE) && !value.equalsIgnoreCase("trailers")) { throw new ProtocolException("Header '%s: %s' is illegal for HTTP/2 messages", header.getName(), header.getValue()); } messageHeaders.add(header); } } if (method == null) { throw new ProtocolException("Mandatory request header '%s' not found", H2PseudoRequestHeaders.METHOD); } if (Method.CONNECT.isSame(method)) { if (authority == null) { throw new ProtocolException("Header '%s' is mandatory for CONNECT request", H2PseudoRequestHeaders.AUTHORITY); } if (scheme != null) { throw new ProtocolException("Header '%s' must not be set for CONNECT request", H2PseudoRequestHeaders.SCHEME); } if (path != null) { throw new ProtocolException("Header '%s' must not be set for CONNECT request", H2PseudoRequestHeaders.PATH); } } else { if (scheme == null) { throw new ProtocolException("Mandatory request header '%s' not found", H2PseudoRequestHeaders.SCHEME); } if (path == null) { throw new ProtocolException("Mandatory request header '%s' not found", H2PseudoRequestHeaders.PATH); } } final HttpRequest httpRequest = new BasicHttpRequest(method, path); httpRequest.setVersion(HttpVersion.HTTP_2); httpRequest.setScheme(scheme); try { httpRequest.setAuthority(URIAuthority.create(authority)); } catch (final URISyntaxException ex) { throw new ProtocolException(ex.getMessage(), ex); } httpRequest.setPath(path); for (int i = 0; i < messageHeaders.size(); i++) { httpRequest.addHeader(messageHeaders.get(i)); } return httpRequest; } @Override public List
convert(final HttpRequest message) throws HttpException { if (TextUtils.isBlank(message.getMethod())) { throw new ProtocolException("Request method is empty"); } final boolean optionMethod = Method.CONNECT.name().equalsIgnoreCase(message.getMethod()); if (optionMethod) { if (message.getAuthority() == null) { throw new ProtocolException("CONNECT request authority is not set"); } if (message.getPath() != null) { throw new ProtocolException("CONNECT request path must be null"); } } else { if (TextUtils.isBlank(message.getScheme())) { throw new ProtocolException("Request scheme is not set"); } if (TextUtils.isBlank(message.getPath())) { throw new ProtocolException("Request path is not set"); } } final List
headers = new ArrayList<>(); headers.add(new BasicHeader(H2PseudoRequestHeaders.METHOD, message.getMethod(), false)); if (optionMethod) { headers.add(new BasicHeader(H2PseudoRequestHeaders.AUTHORITY, message.getAuthority(), false)); } else { headers.add(new BasicHeader(H2PseudoRequestHeaders.SCHEME, message.getScheme(), false)); if (message.getAuthority() != null) { headers.add(new BasicHeader(H2PseudoRequestHeaders.AUTHORITY, message.getAuthority(), false)); } headers.add(new BasicHeader(H2PseudoRequestHeaders.PATH, message.getPath(), false)); } for (final Iterator
it = message.headerIterator(); it.hasNext(); ) { final Header header = it.next(); final String name = header.getName(); final String value = header.getValue(); if (name.startsWith(":")) { throw new ProtocolException("Header name '%s' is invalid", name); } if (name.equalsIgnoreCase(HttpHeaders.CONNECTION) || name.equalsIgnoreCase(HttpHeaders.KEEP_ALIVE) || name.equalsIgnoreCase(HttpHeaders.PROXY_CONNECTION) || name.equalsIgnoreCase(HttpHeaders.TRANSFER_ENCODING) || name.equalsIgnoreCase(HttpHeaders.HOST) || name.equalsIgnoreCase(HttpHeaders.UPGRADE)) { throw new ProtocolException("Header '%s: %s' is illegal for HTTP/2 messages", header.getName(), header.getValue()); } if (name.equalsIgnoreCase(HttpHeaders.TE) && !value.equalsIgnoreCase("trailers")) { throw new ProtocolException("Header '%s: %s' is illegal for HTTP/2 messages", header.getName(), header.getValue()); } headers.add(new BasicHeader(TextUtils.toLowerCase(name), value)); } return headers; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/io/0040775 0000000 0000000 00000000000 14245617503 022531 5ustar000000000 0000000 httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/io/FrameOutputBuffer.java0100664 0000000 0000000 00000010311 14245617503 026772 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.io; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.H2TransportMetrics; import org.apache.hc.core5.http2.frame.FrameConsts; import org.apache.hc.core5.http2.frame.FrameFlag; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics; import org.apache.hc.core5.util.Args; /** * Frame output buffer for HTTP/2 blocking connections. * * @since 5.0 */ public final class FrameOutputBuffer { private final BasicH2TransportMetrics metrics; private final int maxFramePayloadSize; private final byte[] buffer; public FrameOutputBuffer(final BasicH2TransportMetrics metrics, final int maxFramePayloadSize) { super(); Args.notNull(metrics, "HTTP2 transport metrics"); Args.positive(maxFramePayloadSize, "Maximum payload size"); this.metrics = metrics; this.maxFramePayloadSize = maxFramePayloadSize; this.buffer = new byte[FrameConsts.HEAD_LEN + maxFramePayloadSize + FrameConsts.MAX_PADDING + 1]; } public FrameOutputBuffer(final int maxFramePayloadSize) { this(new BasicH2TransportMetrics(), maxFramePayloadSize); } public void write(final RawFrame frame, final OutputStream outStream) throws IOException { if (frame == null) { return; } final int type = frame.getType(); final long streamId = frame.getStreamId(); final int flags = frame.getFlags(); final ByteBuffer payload = frame.getPayload(); final int payloadLen = payload != null ? payload.remaining() : 0; if (payload != null && payload.remaining() > maxFramePayloadSize) { throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Frame size exceeds maximum"); } buffer[0] = (byte) (payloadLen >> 16 & 0xff); buffer[1] = (byte) (payloadLen >> 8 & 0xff); buffer[2] = (byte) (payloadLen & 0xff); buffer[3] = (byte) (type & 0xff); buffer[4] = (byte) (flags & 0xff); buffer[5] = (byte) (streamId >> 24 & 0xff); buffer[6] = (byte) (streamId >> 16 & 0xff); buffer[7] = (byte) (streamId >> 8 & 0xff); buffer[8] = (byte) (streamId & 0xff); int frameLen = FrameConsts.HEAD_LEN; int padding = 0; if ((flags & FrameFlag.PADDED.getValue()) > 0) { padding = 16; buffer[9] = (byte) (padding & 0xff); frameLen++; } if (payload != null) { payload.get(buffer, frameLen, payload.remaining()); frameLen += payloadLen; } for (int i = 0; i < padding; i++) { buffer[frameLen++] = 0; } outStream.write(buffer, 0, frameLen); metrics.incrementFramesTransferred(); metrics.incrementBytesTransferred(frameLen); } public H2TransportMetrics getMetrics() { return metrics; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/io/FrameInputBuffer.java0100664 0000000 0000000 00000012303 14245617503 026574 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.io; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2CorruptFrameException; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.H2TransportMetrics; import org.apache.hc.core5.http2.frame.FrameConsts; import org.apache.hc.core5.http2.frame.FrameFlag; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics; import org.apache.hc.core5.util.Args; /** * Frame input buffer for HTTP/2 blocking connections. * * @since 5.0 */ public final class FrameInputBuffer { private final BasicH2TransportMetrics metrics; private final int maxFramePayloadSize; private final byte[] buffer; private int off; private int dataLen; FrameInputBuffer(final BasicH2TransportMetrics metrics, final int bufferLen, final int maxFramePayloadSize) { Args.notNull(metrics, "HTTP2 transport metrics"); Args.positive(maxFramePayloadSize, "Maximum payload size"); this.metrics = metrics; this.maxFramePayloadSize = maxFramePayloadSize; this.buffer = new byte[bufferLen]; this.dataLen = 0; } public FrameInputBuffer(final BasicH2TransportMetrics metrics, final int maxFramePayloadSize) { this(metrics, FrameConsts.HEAD_LEN + maxFramePayloadSize, maxFramePayloadSize); } public FrameInputBuffer(final int maxFramePayloadSize) { this(new BasicH2TransportMetrics(), maxFramePayloadSize); } boolean hasData() { return this.dataLen > 0; } void fillBuffer(final InputStream inStream, final int requiredLen) throws IOException { while (dataLen < requiredLen) { if (off > 0) { System.arraycopy(buffer, off, buffer, 0, dataLen); off = 0; } final int bytesRead = inStream.read(buffer, off + dataLen, buffer.length - dataLen); if (bytesRead == -1) { if (dataLen > 0) { throw new H2CorruptFrameException("Corrupt or incomplete HTTP2 frame"); } throw new ConnectionClosedException(); } dataLen += bytesRead; this.metrics.incrementBytesTransferred(bytesRead); } } public RawFrame read(final InputStream inStream) throws IOException { fillBuffer(inStream, FrameConsts.HEAD_LEN); final int payloadOff = FrameConsts.HEAD_LEN; final int payloadLen = (buffer[off] & 0xff) << 16 | (buffer[off + 1] & 0xff) << 8 | (buffer[off + 2] & 0xff); final int type = buffer[off + 3] & 0xff; final int flags = buffer[off + 4] & 0xff; final int streamId = Math.abs(buffer[off + 5] & 0xff) << 24 | (buffer[off + 6] & 0xff << 16) | (buffer[off + 7] & 0xff) << 8 | (buffer[off + 8] & 0xff); if (payloadLen > maxFramePayloadSize) { throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Frame size exceeds maximum"); } final int frameLen = payloadOff + payloadLen; fillBuffer(inStream, frameLen); if ((flags & FrameFlag.PADDED.getValue()) > 0) { if (payloadLen == 0) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Inconsistent padding"); } final int padding = buffer[off + FrameConsts.HEAD_LEN] & 0xff; if (payloadLen < padding + 1) { throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Inconsistent padding"); } } final ByteBuffer payload = payloadLen > 0 ? ByteBuffer.wrap(buffer, off + payloadOff, payloadLen) : null; final RawFrame frame = new RawFrame(type, flags, streamId, payload); off += frameLen; dataLen -= frameLen; this.metrics.incrementFramesTransferred(); return frame; } public H2TransportMetrics getMetrics() { return metrics; } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/io/FrameOutputStream.java0100664 0000000 0000000 00000006440 14245617503 027024 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.io; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import org.apache.hc.core5.util.Args; /** * @since 5.0 */ abstract class FrameOutputStream extends OutputStream { private final OutputStream outputStream; private final byte[] cache; private int cachePosition; public FrameOutputStream(final int minChunkSize, final OutputStream outputStream) { super(); this.outputStream = Args.notNull(outputStream, "Output stream"); this.cache = new byte[minChunkSize]; } protected abstract void write(final ByteBuffer src, final boolean endStream, final OutputStream outputStream) throws IOException; private void flushCache(final boolean endStream) throws IOException { if (this.cachePosition > 0) { write(ByteBuffer.wrap(this.cache, 0, this.cachePosition), endStream, this.outputStream); this.cachePosition = 0; } } @Override public void write(final int b) throws IOException { this.cache[this.cachePosition] = (byte) b; this.cachePosition++; if (this.cachePosition == this.cache.length) { flushCache(false); } } @Override public void write(final byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(final byte[] src, final int off, final int len) throws IOException { if (len >= this.cache.length - this.cachePosition) { flushCache(false); write(ByteBuffer.wrap(src, off, len), false, this.outputStream); } else { System.arraycopy(src, off, cache, this.cachePosition, len); this.cachePosition += len; } } @Override public void flush() throws IOException { flushCache(false); this.outputStream.flush(); } @Override public void close() throws IOException { if (this.cachePosition > 0) { flushCache(true); } else { write(null, true, this.outputStream); } flushCache(true); this.outputStream.flush(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/H2Processors.java0100664 0000000 0000000 00000007355 14403631147 025324 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.HttpProcessorBuilder; import org.apache.hc.core5.http.protocol.RequestExpectContinue; import org.apache.hc.core5.http.protocol.RequestUserAgent; import org.apache.hc.core5.http.protocol.ResponseDate; import org.apache.hc.core5.http.protocol.ResponseServer; import org.apache.hc.core5.http2.protocol.H2RequestConnControl; import org.apache.hc.core5.http2.protocol.H2RequestContent; import org.apache.hc.core5.http2.protocol.H2RequestTargetHost; import org.apache.hc.core5.http2.protocol.H2RequestValidateHost; import org.apache.hc.core5.http2.protocol.H2ResponseConnControl; import org.apache.hc.core5.http2.protocol.H2ResponseContent; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.VersionInfo; /** * @since 5.0 */ public final class H2Processors { private final static String SOFTWARE = "Apache-HttpCore"; public static HttpProcessorBuilder customServer(final String serverInfo) { return HttpProcessorBuilder.create() .addAll( new ResponseDate(), new ResponseServer(!TextUtils.isBlank(serverInfo) ? serverInfo : VersionInfo.getSoftwareInfo(SOFTWARE, "org.apache.hc.core5", H2Processors.class)), H2ResponseContent.INSTANCE, H2ResponseConnControl.INSTANCE) .addAll( H2RequestValidateHost.INSTANCE); } public static HttpProcessor server(final String serverInfo) { return customServer(serverInfo).build(); } public static HttpProcessor server() { return customServer(null).build(); } public static HttpProcessorBuilder customClient(final String agentInfo) { return HttpProcessorBuilder.create() .addAll( H2RequestContent.INSTANCE, H2RequestTargetHost.INSTANCE, H2RequestConnControl.INSTANCE, new RequestUserAgent(!TextUtils.isBlank(agentInfo) ? agentInfo : VersionInfo.getSoftwareInfo(SOFTWARE, "org.apache.hc.core5", HttpProcessors.class)), RequestExpectContinue.INSTANCE); } public static HttpProcessor client(final String agentInfo) { return customClient(agentInfo).build(); } public static HttpProcessor client() { return customClient(null).build(); } } httpcore5-h2/src/main/java/org/apache/hc/core5/http2/H2MessageConverter.java0100664 0000000 0000000 00000004371 14245617503 025474 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpMessage; /** * Abstract message converter intended to convert from a list of HTTP/2 headers to object * representing an HTTP message and from an object representing an HTTP message to a list * of HTTP/2 headers. * * @param represents {@link HttpMessage} * * @since 5.0 */ public interface H2MessageConverter { /** * Converts given list of HTTP/2 headers to a {@link HttpMessage}. * * @param headers list of HTTP/2 headers * @return HTTP message * @throws HttpException in case of HTTP protocol violation */ T convert(List
headers) throws HttpException; /** * Converts given {@link HttpMessage} to a list of HTTP/2 headers. * * @param message HTTP message * @return list of HTTP/2 headers * @throws HttpException in case of HTTP protocol violation */ List
convert(T message) throws HttpException; } httpcore5-h2/src/test/0040775 0000000 0000000 00000000000 14245617503 013613 5ustar000000000 0000000 httpcore5-h2/src/test/java/0040775 0000000 0000000 00000000000 14245617503 014534 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/0040775 0000000 0000000 00000000000 14245617503 015323 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/0040775 0000000 0000000 00000000000 14245617503 016544 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/0040775 0000000 0000000 00000000000 14245617503 017136 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/0040775 0000000 0000000 00000000000 14245617503 020153 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/http2/0040775 0000000 0000000 00000000000 14403631147 021210 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/http2/config/0040775 0000000 0000000 00000000000 14403631147 022455 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/http2/config/H2ConfigTest.java0100664 0000000 0000000 00000006525 14403631147 025564 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.config; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class H2ConfigTest { @Test void builder() { // Create and start requester final H2Config h2Config = H2Config.custom() .setPushEnabled(false) .build(); assertNotNull(h2Config); } @Test void checkValues() { // Create and start requester final H2Config h2Config = H2Config.custom() .setHeaderTableSize(1) .setMaxConcurrentStreams(1) .setMaxFrameSize(16384) .setPushEnabled(true) .setCompressionEnabled(true) .build(); assertEquals(1, h2Config.getHeaderTableSize()); assertEquals(1, h2Config.getMaxConcurrentStreams()); assertEquals(16384, h2Config.getMaxFrameSize()); assertTrue(h2Config.isPushEnabled()); assertTrue(h2Config.isCompressionEnabled()); } @Test void copy() { // Create and start requester final H2Config h2Config = H2Config.custom() .setHeaderTableSize(1) .setMaxConcurrentStreams(1) .setMaxFrameSize(16384) .setPushEnabled(true) .setCompressionEnabled(true) .build(); final H2Config.Builder builder = H2Config.copy(h2Config); final H2Config h2Config2 = builder.build(); assertAll( () -> assertEquals(h2Config.getHeaderTableSize(), h2Config2.getHeaderTableSize()), () -> assertEquals(h2Config.getInitialWindowSize(), h2Config2.getInitialWindowSize()), () -> assertEquals(h2Config.getMaxConcurrentStreams(), h2Config2.getMaxConcurrentStreams()), () -> assertEquals(h2Config.getMaxFrameSize(), h2Config2.getMaxFrameSize()), () -> assertEquals(h2Config.getMaxHeaderListSize(), h2Config2.getMaxHeaderListSize()) ); } }httpcore5-h2/src/test/java/org/apache/hc/core5/http2/frame/0040775 0000000 0000000 00000000000 14403631147 022302 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/http2/frame/TestDefaultFrameFactory.java0100664 0000000 0000000 00000010437 14403631147 027676 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.config.H2Param; import org.apache.hc.core5.http2.config.H2Setting; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDefaultFrameFactory { @Test public void testDataFrame() throws Exception { final FrameFactory frameFactory = new DefaultFrameFactory(); final byte[] data = new byte[]{'a', 'b', 'c', 'd', 'e', 'f'}; final Frame dataFrame = frameFactory.createData(23, ByteBuffer.wrap(data), true); Assertions.assertEquals(FrameType.DATA.value, dataFrame.getType()); Assertions.assertEquals(23, dataFrame.getStreamId()); Assertions.assertEquals(1L, dataFrame.getFlags()); } @Test public void testSettingFrame() throws Exception { final FrameFactory frameFactory = new DefaultFrameFactory(); final Frame settingsFrame = frameFactory.createSettings( new H2Setting(H2Param.HEADER_TABLE_SIZE, 1024), new H2Setting(H2Param.MAX_CONCURRENT_STREAMS, 1)); Assertions.assertEquals(FrameType.SETTINGS.value, settingsFrame.getType()); Assertions.assertEquals(0, settingsFrame.getStreamId()); Assertions.assertEquals(0, settingsFrame.getFlags()); final ByteBuffer payload = settingsFrame.getPayload(); Assertions.assertNotNull(payload); Assertions.assertEquals(12, payload.remaining()); } @Test public void testResetStreamFrame() throws Exception { final FrameFactory frameFactory = new DefaultFrameFactory(); final Frame rstStreamFrame = frameFactory.createResetStream(12, H2Error.INTERNAL_ERROR); Assertions.assertEquals(FrameType.RST_STREAM.value, rstStreamFrame.getType()); Assertions.assertEquals(12, rstStreamFrame.getStreamId()); Assertions.assertEquals(0, rstStreamFrame.getFlags()); final ByteBuffer payload = rstStreamFrame.getPayload(); Assertions.assertNotNull(payload); Assertions.assertEquals(4, payload.remaining()); Assertions.assertEquals(H2Error.INTERNAL_ERROR.getCode(), payload.getInt()); } @Test public void testGoAwayFrame() throws Exception { final FrameFactory frameFactory = new DefaultFrameFactory(); final Frame goAwayFrame = frameFactory.createGoAway(13, H2Error.INTERNAL_ERROR, "Oopsie"); Assertions.assertEquals(FrameType.GOAWAY.value, goAwayFrame.getType()); Assertions.assertEquals(0, goAwayFrame.getStreamId()); Assertions.assertEquals(0, goAwayFrame.getFlags()); final ByteBuffer payload = goAwayFrame.getPayload(); Assertions.assertNotNull(payload); Assertions.assertEquals(13, payload.getInt()); Assertions.assertEquals(H2Error.INTERNAL_ERROR.getCode(), payload.getInt()); final byte[] tmp = new byte[payload.remaining()]; payload.get(tmp); Assertions.assertEquals("Oopsie", new String(tmp, StandardCharsets.US_ASCII)); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/frame/TestFrameFlag.java0100664 0000000 0000000 00000003042 14403631147 025625 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestFrameFlag { @Test public void testFrameFlagBasics() throws Exception { final int flags = FrameFlag.of(FrameFlag.END_STREAM, FrameFlag.PADDED, FrameFlag.PRIORITY); Assertions.assertEquals(0x01 | 0x08 | 0x20, flags); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/frame/TestH2Settings.java0100664 0000000 0000000 00000004455 14403631147 026004 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.frame; import org.apache.hc.core5.http2.config.H2Param; import org.apache.hc.core5.http2.config.H2Setting; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestH2Settings { @Test public void testH2ParamBasics() throws Exception { for (final H2Param param: H2Param.values()) { Assertions.assertEquals(param, H2Param.valueOf(param.getCode())); Assertions.assertEquals(param.name(), H2Param.toString(param.getCode())); } Assertions.assertNull(H2Param.valueOf(0)); Assertions.assertNull(H2Param.valueOf(10)); Assertions.assertEquals("0", H2Param.toString(0)); Assertions.assertEquals("10", H2Param.toString(10)); } @Test public void testH2SettingBasics() throws Exception { final H2Setting setting1 = new H2Setting(H2Param.ENABLE_PUSH, 0); final H2Setting setting2 = new H2Setting(H2Param.INITIAL_WINDOW_SIZE, 1024); Assertions.assertEquals("ENABLE_PUSH: 0", setting1.toString()); Assertions.assertEquals("INITIAL_WINDOW_SIZE: 1024", setting2.toString()); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/hpack/0040775 0000000 0000000 00000000000 14403631147 022276 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/http2/hpack/TestFifoLinkedList.java0100664 0000000 0000000 00000015444 14403631147 026654 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestFifoLinkedList { @Test public void testAddRemoveCycle() throws Exception { final FifoLinkedList fifoLinkedList = new FifoLinkedList(); final HPackHeader h1 = new HPackHeader("h", "1"); final HPackHeader h2 = new HPackHeader("h", "2"); final HPackHeader h3 = new HPackHeader("h", "3"); final HPackHeader h4 = new HPackHeader("h", "4"); for (int i = 0; i < 5; i++) { Assertions.assertEquals(0, fifoLinkedList.size()); Assertions.assertSame(null, fifoLinkedList.getFirst()); Assertions.assertSame(null, fifoLinkedList.getLast()); fifoLinkedList.addFirst(h1); Assertions.assertEquals(1, fifoLinkedList.size()); Assertions.assertSame(h1, fifoLinkedList.getFirst()); Assertions.assertSame(h1, fifoLinkedList.getLast()); fifoLinkedList.addFirst(h2); Assertions.assertEquals(2, fifoLinkedList.size()); Assertions.assertSame(h2, fifoLinkedList.getFirst()); Assertions.assertSame(h1, fifoLinkedList.getLast()); fifoLinkedList.addFirst(h3); Assertions.assertEquals(3, fifoLinkedList.size()); Assertions.assertSame(h3, fifoLinkedList.getFirst()); Assertions.assertSame(h1, fifoLinkedList.getLast()); fifoLinkedList.addFirst(h4); Assertions.assertEquals(4, fifoLinkedList.size()); Assertions.assertSame(h4, fifoLinkedList.getFirst()); Assertions.assertSame(h1, fifoLinkedList.getLast()); fifoLinkedList.removeLast(); Assertions.assertEquals(3, fifoLinkedList.size()); Assertions.assertSame(h4, fifoLinkedList.getFirst()); Assertions.assertSame(h2, fifoLinkedList.getLast()); fifoLinkedList.removeLast(); Assertions.assertEquals(2, fifoLinkedList.size()); Assertions.assertSame(h4, fifoLinkedList.getFirst()); Assertions.assertSame(h3, fifoLinkedList.getLast()); fifoLinkedList.removeLast(); Assertions.assertEquals(1, fifoLinkedList.size()); Assertions.assertSame(h4, fifoLinkedList.getFirst()); Assertions.assertSame(h4, fifoLinkedList.getLast()); fifoLinkedList.removeLast(); Assertions.assertEquals(0, fifoLinkedList.size()); Assertions.assertSame(null, fifoLinkedList.getFirst()); Assertions.assertSame(null, fifoLinkedList.getLast()); } } @Test public void testGetIndex() throws Exception { final FifoLinkedList fifoLinkedList = new FifoLinkedList(); final HPackHeader h1 = new HPackHeader("h", "1"); final HPackHeader h2 = new HPackHeader("h", "2"); final HPackHeader h3 = new HPackHeader("h", "3"); final HPackHeader h4 = new HPackHeader("h", "4"); final FifoLinkedList.InternalNode node1 = fifoLinkedList.addFirst(h1); final FifoLinkedList.InternalNode node2 = fifoLinkedList.addFirst(h2); final FifoLinkedList.InternalNode node3 = fifoLinkedList.addFirst(h3); final FifoLinkedList.InternalNode node4 = fifoLinkedList.addFirst(h4); Assertions.assertEquals(0, fifoLinkedList.getIndex(node4)); Assertions.assertEquals(1, fifoLinkedList.getIndex(node3)); Assertions.assertEquals(2, fifoLinkedList.getIndex(node2)); Assertions.assertEquals(3, fifoLinkedList.getIndex(node1)); Assertions.assertEquals(4, fifoLinkedList.size()); Assertions.assertSame(h4, fifoLinkedList.get(0)); Assertions.assertSame(h3, fifoLinkedList.get(1)); Assertions.assertSame(h2, fifoLinkedList.get(2)); Assertions.assertSame(h1, fifoLinkedList.get(3)); fifoLinkedList.removeLast(); Assertions.assertEquals(0, fifoLinkedList.getIndex(node4)); Assertions.assertEquals(1, fifoLinkedList.getIndex(node3)); Assertions.assertEquals(2, fifoLinkedList.getIndex(node2)); Assertions.assertEquals(-1, fifoLinkedList.getIndex(node1)); Assertions.assertEquals(3, fifoLinkedList.size()); Assertions.assertSame(h4, fifoLinkedList.get(0)); Assertions.assertSame(h3, fifoLinkedList.get(1)); Assertions.assertSame(h2, fifoLinkedList.get(2)); fifoLinkedList.removeLast(); Assertions.assertEquals(0, fifoLinkedList.getIndex(node4)); Assertions.assertEquals(1, fifoLinkedList.getIndex(node3)); Assertions.assertEquals(-1, fifoLinkedList.getIndex(node2)); Assertions.assertEquals(-1, fifoLinkedList.getIndex(node1)); Assertions.assertEquals(2, fifoLinkedList.size()); Assertions.assertSame(h4, fifoLinkedList.get(0)); Assertions.assertSame(h3, fifoLinkedList.get(1)); fifoLinkedList.removeLast(); Assertions.assertEquals(0, fifoLinkedList.getIndex(node4)); Assertions.assertEquals(-1, fifoLinkedList.getIndex(node3)); Assertions.assertEquals(-1, fifoLinkedList.getIndex(node2)); Assertions.assertEquals(-1, fifoLinkedList.getIndex(node1)); Assertions.assertEquals(1, fifoLinkedList.size()); Assertions.assertSame(h4, fifoLinkedList.get(0)); fifoLinkedList.removeLast(); Assertions.assertEquals(-1, fifoLinkedList.getIndex(node4)); Assertions.assertEquals(-1, fifoLinkedList.getIndex(node3)); Assertions.assertEquals(-1, fifoLinkedList.getIndex(node2)); Assertions.assertEquals(-1, fifoLinkedList.getIndex(node1)); Assertions.assertEquals(0, fifoLinkedList.size()); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/hpack/TestOutboundDynamicTable.java0100664 0000000 0000000 00000005012 14403631147 030050 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestOutboundDynamicTable { @Test public void testBasics() throws Exception { final OutboundDynamicTable table = new OutboundDynamicTable(); Assertions.assertEquals(Integer.MAX_VALUE, table.getMaxSize()); Assertions.assertEquals(0, table.getCurrentSize()); final HPackHeader header1 = new HPackHeader("h", "1"); table.add(header1); Assertions.assertEquals(1, table.dynamicLength()); Assertions.assertEquals(61, table.staticLength()); Assertions.assertEquals(62, table.length()); Assertions.assertSame(header1, table.getHeader(62)); Assertions.assertEquals(34, table.getCurrentSize()); } @Test public void testEviction() throws Exception { final OutboundDynamicTable table = new OutboundDynamicTable(); table.add(new HPackHeader("h", "1")); table.add(new HPackHeader("h", "2")); Assertions.assertEquals(68, table.getCurrentSize()); table.setMaxSize(256); Assertions.assertEquals(68, table.getCurrentSize()); table.setMaxSize(67); Assertions.assertEquals(34, table.getCurrentSize()); table.setMaxSize(10); Assertions.assertEquals(0, table.getCurrentSize()); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/hpack/TestInboundDynamicTable.java0100664 0000000 0000000 00000005005 14403631147 027651 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestInboundDynamicTable { @Test public void testBasics() throws Exception { final InboundDynamicTable table = new InboundDynamicTable(); Assertions.assertEquals(Integer.MAX_VALUE, table.getMaxSize()); Assertions.assertEquals(0, table.getCurrentSize()); final HPackHeader header1 = new HPackHeader("h", "1"); table.add(header1); Assertions.assertEquals(1, table.dynamicLength()); Assertions.assertEquals(61, table.staticLength()); Assertions.assertEquals(62, table.length()); Assertions.assertSame(header1, table.getHeader(62)); Assertions.assertEquals(34, table.getCurrentSize()); } @Test public void testEviction() throws Exception { final InboundDynamicTable table = new InboundDynamicTable(); table.add(new HPackHeader("h", "1")); table.add(new HPackHeader("h", "2")); Assertions.assertEquals(68, table.getCurrentSize()); table.setMaxSize(256); Assertions.assertEquals(68, table.getCurrentSize()); table.setMaxSize(67); Assertions.assertEquals(34, table.getCurrentSize()); table.setMaxSize(10); Assertions.assertEquals(0, table.getCurrentSize()); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/hpack/TestHPackCoding.java0100664 0000000 0000000 00000154511 14403631147 026117 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.util.ByteArrayBuffer; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestHPackCoding { @Test public void testIntegerEncodingRFC7541Examples() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(16); HPackEncoder.encodeInt(buffer, 5, 10, 0x0); Assertions.assertEquals(1, buffer.length()); Assertions.assertEquals(0b00001010, buffer.byteAt(0) & 0xFF); buffer.clear(); HPackEncoder.encodeInt(buffer, 5, 1337, 0x0); Assertions.assertEquals(3, buffer.length()); Assertions.assertEquals(0b00011111, buffer.byteAt(0) & 0xFF); Assertions.assertEquals(0b10011010, buffer.byteAt(1) & 0xFF); Assertions.assertEquals(0b00001010, buffer.byteAt(2) & 0xFF); buffer.clear(); HPackEncoder.encodeInt(buffer, 8, 42, 0x0); Assertions.assertEquals(1, buffer.length()); Assertions.assertEquals(0b00101010, buffer.byteAt(0) & 0xFF); } static ByteBuffer wrap(final ByteArrayBuffer src) { // Use buffers with array offsets to verify correcness in additional cases final byte[] originalArray = src.array(); final byte[] newArray = new byte[originalArray.length + 2]; System.arraycopy(originalArray, 0, newArray, 1, src.length()); return ByteBuffer.wrap(newArray, 1, src.length()).slice(); } private static byte[] toArray(final ByteBuffer buffer) { final byte[] result = new byte[buffer.remaining()]; buffer.get(result); return result; } @Test public void testIntegerCoding() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(16); for (int n = 4; n <= 8; n++) { buffer.clear(); HPackEncoder.encodeInt(buffer, n, 10, 0x0); Assertions.assertEquals(10, HPackDecoder.decodeInt(wrap(buffer), n)); buffer.clear(); HPackEncoder.encodeInt(buffer, n, 123456, 0x0); Assertions.assertEquals(123456, HPackDecoder.decodeInt(wrap(buffer), n)); buffer.clear(); HPackEncoder.encodeInt(buffer, n, Integer.MAX_VALUE, 0x0); Assertions.assertEquals(Integer.MAX_VALUE, HPackDecoder.decodeInt(wrap(buffer), n)); } } @Test public void testIntegerCodingLimit() throws Exception { final ByteBuffer src1 = createByteBuffer(0x7f, 0x80, 0xff, 0xff, 0xff, 0x07); Assertions.assertEquals(Integer.MAX_VALUE, HPackDecoder.decodeInt(src1, 7)); final ByteBuffer src2 = createByteBuffer(0x7f, 0x80, 0xff, 0xff, 0xff, 0x08); try { HPackDecoder.decodeInt(src2, 7); } catch (final HPackException expected) { } final ByteBuffer src3 = createByteBuffer(0x7f, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01); try { HPackDecoder.decodeInt(src3, 7); } catch (final HPackException expected) { } } private static ByteBuffer createByteBuffer(final int... bytes) { final ByteBuffer buffer = ByteBuffer.allocate(bytes.length); for (final int b : bytes) { buffer.put((byte) b); } buffer.flip(); return buffer; } @Test public void testPlainStringDecoding() throws Exception { final ByteBuffer src = createByteBuffer( 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79); final ByteArrayBuffer buffer = new ByteArrayBuffer(16); HPackDecoder.decodePlainString(buffer, src); Assertions.assertEquals("custom-key", new String(buffer.array(), 0, buffer.length(), StandardCharsets.US_ASCII)); Assertions.assertFalse(src.hasRemaining(), "Decoding completed"); } @Test public void testPlainStringDecodingRemainingContent() throws Exception { final ByteBuffer src = createByteBuffer( 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x01, 0x01, 0x01, 0x01); final ByteArrayBuffer buffer = new ByteArrayBuffer(16); HPackDecoder.decodePlainString(buffer, src); Assertions.assertEquals(new String(buffer.array(), 0, buffer.length(), StandardCharsets.US_ASCII), "custom-key"); Assertions.assertEquals(4, src.remaining()); } @Test public void testPlainStringDecodingReadOnly() throws Exception { final ByteBuffer src = createByteBuffer( 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x50, 0x50, 0x50, 0x50); final ByteBuffer srcRO = src.asReadOnlyBuffer(); final ByteArrayBuffer buffer = new ByteArrayBuffer(16); HPackDecoder.decodePlainString(buffer, srcRO); Assertions.assertEquals("custom-key", new String(buffer.array(), 0, buffer.length(), StandardCharsets.US_ASCII)); Assertions.assertEquals(4, srcRO.remaining()); } @Test public void testPlainStringDecodingTruncated() throws Exception { final ByteBuffer src = createByteBuffer( 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65); final ByteArrayBuffer buffer = new ByteArrayBuffer(16); Assertions.assertThrows(HPackException.class, () -> HPackDecoder.decodePlainString(buffer, src)); } @Test public void testHuffmanDecodingRFC7541Examples() throws Exception { final ByteBuffer src = createByteBuffer( 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff); final ByteArrayBuffer buffer = new ByteArrayBuffer(16); HPackDecoder.decodeHuffman(buffer, src); Assertions.assertEquals(new String(buffer.array(), 0, buffer.length(), StandardCharsets.US_ASCII), "www.example.com"); Assertions.assertFalse(src.hasRemaining(), "Decoding completed"); } private static ByteBuffer createByteBuffer(final String s, final Charset charset) { return ByteBuffer.wrap(s.getBytes(charset)); } @Test public void testHuffmanEncoding() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(16); HPackEncoder.encodeHuffman(buffer, createByteBuffer("www.example.com", StandardCharsets.US_ASCII)); final ByteBuffer expected = createByteBuffer( 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff); Assertions.assertArrayEquals(toArray(expected), buffer.toByteArray()); } @Test public void testBasicStringCoding() throws Exception { final HPackEncoder encoder = new HPackEncoder(StandardCharsets.US_ASCII); final HPackDecoder decoder = new HPackDecoder(StandardCharsets.US_ASCII); final ByteArrayBuffer buffer = new ByteArrayBuffer(16); encoder.encodeString(buffer, "this and that", false); final StringBuilder strBuf = new StringBuilder(); decoder.decodeString(wrap(buffer), strBuf); Assertions.assertEquals("this and that", strBuf.toString()); buffer.clear(); strBuf.setLength(0); encoder.encodeString(buffer, "this and that and Huffman", true); decoder.decodeString(wrap(buffer), strBuf); Assertions.assertEquals("this and that and Huffman", strBuf.toString()); } static final int SWISS_GERMAN_HELLO[] = { 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4 }; static final int RUSSIAN_HELLO[] = { 0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438, 0x432, 0x435, 0x442 }; private static String constructHelloString(final int[] raw, final int n) { final StringBuilder buffer = new StringBuilder(); for (int j = 0; j < n; j++) { if (j > 0) { buffer.append("; "); } for (int i = 0; i < raw.length; i++) { buffer.append((char) raw[i]); } } return buffer.toString(); } @Test public void testComplexStringCoding1() throws Exception { for (final Charset charset : new Charset[]{StandardCharsets.ISO_8859_1, StandardCharsets.UTF_8, StandardCharsets.UTF_16}) { final ByteArrayBuffer buffer = new ByteArrayBuffer(16); final StringBuilder strBuf = new StringBuilder(); final HPackEncoder encoder = new HPackEncoder(charset); final HPackDecoder decoder = new HPackDecoder(charset); for (int n = 0; n < 10; n++) { final String hello = constructHelloString(SWISS_GERMAN_HELLO, 1 + 10 * n); for (final boolean b : new boolean[]{false, true}) { buffer.clear(); encoder.encodeString(buffer, hello, b); strBuf.setLength(0); decoder.decodeString(wrap(buffer), strBuf); final String helloBack = strBuf.toString(); Assertions.assertEquals(hello, helloBack, "charset: " + charset + "; huffman: " + b); } } } } @Test public void testComplexStringCoding2() throws Exception { for (final Charset charset : new Charset[]{Charset.forName("KOI8-R"), StandardCharsets.UTF_8, StandardCharsets.UTF_16}) { final ByteArrayBuffer buffer = new ByteArrayBuffer(16); final StringBuilder strBuf = new StringBuilder(); final HPackEncoder encoder = new HPackEncoder(charset); final HPackDecoder decoder = new HPackDecoder(charset); for (int n = 0; n < 10; n++) { final String hello = constructHelloString(RUSSIAN_HELLO, 1 + 10 * n); for (final boolean b : new boolean[]{false, true}) { buffer.clear(); strBuf.setLength(0); encoder.encodeString(buffer, hello, b); decoder.decodeString(wrap(buffer), strBuf); final String helloBack = strBuf.toString(); Assertions.assertEquals(hello, helloBack, "charset: " + charset + "; huffman: " + b); } } } } private static void assertHeaderEquals(final Header expected, final Header actual) { Assertions.assertNotNull(actual); Assertions.assertEquals(expected.getName(), actual.getName(), "Header name"); Assertions.assertEquals(expected.getValue(), actual.getValue(), "Header value"); Assertions.assertEquals(expected.isSensitive(), actual.isSensitive(), "Header sensitive flag"); } @Test public void testLiteralHeaderWithIndexingDecodingRFC7541Examples() throws Exception { final ByteBuffer src = createByteBuffer( 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72); final InboundDynamicTable dynamicTable = new InboundDynamicTable(); final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII); final Header header = decoder.decodeLiteralHeader(src, HPackRepresentation.WITH_INDEXING); assertHeaderEquals(new BasicHeader("custom-key", "custom-header"), header); Assertions.assertFalse(src.hasRemaining(), "Decoding completed"); Assertions.assertEquals(1, dynamicTable.dynamicLength()); assertHeaderEquals(header, dynamicTable.getDynamicEntry(0)); } @Test public void testLiteralHeaderWithoutIndexingDecodingRFC7541Examples() throws Exception { final ByteBuffer src = createByteBuffer( 0x04, 0x0c, 0x2f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x70, 0x61, 0x74, 0x68); final InboundDynamicTable dynamicTable = new InboundDynamicTable(); final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII); final Header header = decoder.decodeLiteralHeader(src, HPackRepresentation.WITHOUT_INDEXING); assertHeaderEquals(new BasicHeader(":path", "/sample/path"), header); Assertions.assertFalse(src.hasRemaining(), "Decoding completed"); Assertions.assertEquals(0, dynamicTable.dynamicLength()); } @Test public void testLiteralHeaderNeverIndexedDecodingRFC7541Examples() throws Exception { final ByteBuffer src = createByteBuffer( 0x10, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74); final InboundDynamicTable dynamicTable = new InboundDynamicTable(); final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII); final Header header = decoder.decodeLiteralHeader(src, HPackRepresentation.NEVER_INDEXED); assertHeaderEquals(new BasicHeader("password", "secret", true), header); Assertions.assertFalse(src.hasRemaining(), "Decoding completed"); Assertions.assertEquals(0, dynamicTable.dynamicLength()); } @Test public void testIndexedHeaderDecodingRFC7541Examples() throws Exception { final ByteBuffer src = createByteBuffer(0x82); final InboundDynamicTable dynamicTable = new InboundDynamicTable(); final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII); final Header header = decoder.decodeIndexedHeader(src); assertHeaderEquals(new BasicHeader(":method", "GET"), header); Assertions.assertFalse(src.hasRemaining(), "Decoding completed"); Assertions.assertEquals(0, dynamicTable.dynamicLength()); } @Test public void testRequestDecodingWithoutHuffmanRFC7541Examples() throws Exception { final ByteBuffer src1 = createByteBuffer( 0x82, 0x86, 0x84, 0x41, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d); final InboundDynamicTable dynamicTable = new InboundDynamicTable(); final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII); final List
headers1 = decoder.decodeHeaders(src1); Assertions.assertEquals(4, headers1.size()); assertHeaderEquals(new BasicHeader(":method", "GET"), headers1.get(0)); assertHeaderEquals(new BasicHeader(":scheme", "http"), headers1.get(1)); assertHeaderEquals(new BasicHeader(":path", "/"), headers1.get(2)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers1.get(3)); Assertions.assertEquals(1, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(0)); Assertions.assertEquals(57, dynamicTable.getCurrentSize()); final ByteBuffer src2 = createByteBuffer( 0x82, 0x86, 0x84, 0xbe, 0x58, 0x08, 0x6e, 0x6f, 0x2d, 0x63, 0x61, 0x63, 0x68, 0x65); final List
headers2 = decoder.decodeHeaders(src2); Assertions.assertEquals(5, headers2.size()); assertHeaderEquals(new BasicHeader(":method", "GET"), headers2.get(0)); assertHeaderEquals(new BasicHeader(":scheme", "http"), headers2.get(1)); assertHeaderEquals(new BasicHeader(":path", "/"), headers2.get(2)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers2.get(3)); assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), headers2.get(4)); Assertions.assertEquals(2, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(1)); Assertions.assertEquals(110, dynamicTable.getCurrentSize()); final ByteBuffer src3 = createByteBuffer( 0x82, 0x87, 0x85, 0xbf, 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65); final List
headers3 = decoder.decodeHeaders(src3); Assertions.assertEquals(5, headers3.size()); assertHeaderEquals(new BasicHeader(":method", "GET"), headers3.get(0)); assertHeaderEquals(new BasicHeader(":scheme", "https"), headers3.get(1)); assertHeaderEquals(new BasicHeader(":path", "/index.html"), headers3.get(2)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers3.get(3)); assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), headers3.get(4)); Assertions.assertEquals(3, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(2)); Assertions.assertEquals(164, dynamicTable.getCurrentSize()); } @Test public void testRequestDecodingWithHuffmanRFC7541Examples() throws Exception { final ByteBuffer src1 = createByteBuffer( 0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff); final InboundDynamicTable dynamicTable = new InboundDynamicTable(); final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII); final List
headers1 = decoder.decodeHeaders(src1); Assertions.assertEquals(4, headers1.size()); assertHeaderEquals(new BasicHeader(":method", "GET"), headers1.get(0)); assertHeaderEquals(new BasicHeader(":scheme", "http"), headers1.get(1)); assertHeaderEquals(new BasicHeader(":path", "/"), headers1.get(2)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers1.get(3)); Assertions.assertEquals(1, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(0)); Assertions.assertEquals(57, dynamicTable.getCurrentSize()); final ByteBuffer src2 = createByteBuffer( 0x82, 0x86, 0x84, 0xbe, 0x58, 0x86, 0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf); final List
headers2 = decoder.decodeHeaders(src2); Assertions.assertEquals(5, headers2.size()); assertHeaderEquals(new BasicHeader(":method", "GET"), headers2.get(0)); assertHeaderEquals(new BasicHeader(":scheme", "http"), headers2.get(1)); assertHeaderEquals(new BasicHeader(":path", "/"), headers2.get(2)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers2.get(3)); assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), headers2.get(4)); Assertions.assertEquals(2, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(1)); Assertions.assertEquals(110, dynamicTable.getCurrentSize()); final ByteBuffer src3 = createByteBuffer( 0x82, 0x87, 0x85, 0xbf, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, 0x89, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, 0xbf); final List
headers3 = decoder.decodeHeaders(src3); Assertions.assertEquals(5, headers3.size()); assertHeaderEquals(new BasicHeader(":method", "GET"), headers3.get(0)); assertHeaderEquals(new BasicHeader(":scheme", "https"), headers3.get(1)); assertHeaderEquals(new BasicHeader(":path", "/index.html"), headers3.get(2)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers3.get(3)); assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), headers3.get(4)); Assertions.assertEquals(3, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(2)); Assertions.assertEquals(164, dynamicTable.getCurrentSize()); } @Test public void testResponseDecodingWithoutHuffmanRFC7541Examples() throws Exception { final ByteBuffer src1 = createByteBuffer( 0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x31, 0x20, 0x47, 0x4d, 0x54, 0x6e, 0x17, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d); final InboundDynamicTable dynamicTable = new InboundDynamicTable(); dynamicTable.setMaxSize(256); final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII); final List
headers1 = decoder.decodeHeaders(src1); Assertions.assertEquals(4, headers1.size()); assertHeaderEquals(new BasicHeader(":status", "302"), headers1.get(0)); assertHeaderEquals(new BasicHeader("cache-control", "private"), headers1.get(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), headers1.get(2)); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers1.get(3)); Assertions.assertEquals(4, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(2)); assertHeaderEquals(new BasicHeader(":status", "302"), dynamicTable.getDynamicEntry(3)); Assertions.assertEquals(222, dynamicTable.getCurrentSize()); final ByteBuffer src2 = createByteBuffer( 0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf); final List
headers2 = decoder.decodeHeaders(src2); Assertions.assertEquals(4, headers2.size()); assertHeaderEquals(new BasicHeader(":status", "307"), headers2.get(0)); assertHeaderEquals(new BasicHeader("cache-control", "private"), headers2.get(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), headers2.get(2)); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers2.get(3)); Assertions.assertEquals(4, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader(":status", "307"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(2)); assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(3)); Assertions.assertEquals(222, dynamicTable.getCurrentSize()); final ByteBuffer src3 = createByteBuffer( 0x88, 0xc1, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x32, 0x20, 0x47, 0x4d, 0x54, 0xc0, 0x5a, 0x04, 0x67, 0x7a, 0x69, 0x70, 0x77, 0x38, 0x66, 0x6f, 0x6f, 0x3d, 0x41, 0x53, 0x44, 0x4a, 0x4b, 0x48, 0x51, 0x4b, 0x42, 0x5a, 0x58, 0x4f, 0x51, 0x57, 0x45, 0x4f, 0x50, 0x49, 0x55, 0x41, 0x58, 0x51, 0x57, 0x45, 0x4f, 0x49, 0x55, 0x3b, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, 0x3d, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31); final List
headers3 = decoder.decodeHeaders(src3); Assertions.assertEquals(6, headers3.size()); assertHeaderEquals(new BasicHeader(":status", "200"), headers3.get(0)); assertHeaderEquals(new BasicHeader("cache-control", "private"), headers3.get(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), headers3.get(2)); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers3.get(3)); assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), headers3.get(4)); assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), headers3.get(5)); Assertions.assertEquals(3, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), dynamicTable.getDynamicEntry(2)); Assertions.assertEquals(215, dynamicTable.getCurrentSize()); } @Test public void testResponseDecodingWithHuffmanRFC7541Examples() throws Exception { final ByteBuffer src1 = createByteBuffer( 0x48, 0x82, 0x64, 0x02, 0x58, 0x85, 0xae, 0xc3, 0x77, 0x1a, 0x4b, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x82, 0xa6, 0x2d, 0x1b, 0xff, 0x6e, 0x91, 0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, 0x8f, 0x0b, 0x97, 0xc8, 0xe9, 0xae, 0x82, 0xae, 0x43, 0xd3); final InboundDynamicTable dynamicTable = new InboundDynamicTable(); dynamicTable.setMaxSize(256); final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII); final List
headers1 = decoder.decodeHeaders(src1); Assertions.assertEquals(4, headers1.size()); assertHeaderEquals(new BasicHeader(":status", "302"), headers1.get(0)); assertHeaderEquals(new BasicHeader("cache-control", "private"), headers1.get(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), headers1.get(2)); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers1.get(3)); Assertions.assertEquals(4, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(2)); assertHeaderEquals(new BasicHeader(":status", "302"), dynamicTable.getDynamicEntry(3)); Assertions.assertEquals(222, dynamicTable.getCurrentSize()); final ByteBuffer src2 = createByteBuffer( 0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf); final List
headers2 = decoder.decodeHeaders(src2); Assertions.assertEquals(4, headers2.size()); assertHeaderEquals(new BasicHeader(":status", "307"), headers2.get(0)); assertHeaderEquals(new BasicHeader("cache-control", "private"), headers2.get(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), headers2.get(2)); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers2.get(3)); Assertions.assertEquals(4, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader(":status", "307"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(2)); assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(3)); Assertions.assertEquals(222, dynamicTable.getCurrentSize()); final ByteBuffer src3 = createByteBuffer( 0x88, 0xc1, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x84, 0xa6, 0x2d, 0x1b, 0xff, 0xc0, 0x5a, 0x83, 0x9b, 0xd9, 0xab, 0x77, 0xad, 0x94, 0xe7, 0x82, 0x1d, 0xd7, 0xf2, 0xe6, 0xc7, 0xb3, 0x35, 0xdf, 0xdf, 0xcd, 0x5b, 0x39, 0x60, 0xd5, 0xaf, 0x27, 0x08, 0x7f, 0x36, 0x72, 0xc1, 0xab, 0x27, 0x0f, 0xb5, 0x29, 0x1f, 0x95, 0x87, 0x31, 0x60, 0x65, 0xc0, 0x03, 0xed, 0x4e, 0xe5, 0xb1, 0x06, 0x3d, 0x50, 0x07); final List
headers3 = decoder.decodeHeaders(src3); Assertions.assertEquals(6, headers3.size()); assertHeaderEquals(new BasicHeader(":status", "200"), headers3.get(0)); assertHeaderEquals(new BasicHeader("cache-control", "private"), headers3.get(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), headers3.get(2)); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers3.get(3)); assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), headers3.get(4)); assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), headers3.get(5)); Assertions.assertEquals(3, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), dynamicTable.getDynamicEntry(2)); Assertions.assertEquals(215, dynamicTable.getCurrentSize()); } private static byte[] createByteArray(final int... bytes) { final byte[] buffer = new byte[bytes.length]; for (int i = 0; i < bytes.length; i++) { buffer[i] = (byte) bytes[i]; } return buffer; } @Test public void testLiteralHeaderWithIndexingEncodingRFC7541Examples() throws Exception { final OutboundDynamicTable dynamicTable = new OutboundDynamicTable(); final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII); final ByteArrayBuffer buf = new ByteArrayBuffer(128); final Header header = new BasicHeader("custom-key", "custom-header"); encoder.encodeLiteralHeader(buf, null, header, HPackRepresentation.WITH_INDEXING, false); final byte[] expected = createByteArray( 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72); Assertions.assertArrayEquals(expected, buf.toByteArray()); } @Test public void testLiteralHeaderWithoutIndexingEncodingRFC7541Examples() throws Exception { final OutboundDynamicTable dynamicTable = new OutboundDynamicTable(); final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII); final ByteArrayBuffer buf = new ByteArrayBuffer(128); final Header header = new BasicHeader(":path", "/sample/path"); encoder.encodeLiteralHeader(buf, new HPackEntry() { @Override public int getIndex() { return 4; } @Override public HPackHeader getHeader() { return new HPackHeader(header); } }, header, HPackRepresentation.WITHOUT_INDEXING, false); final byte[] expected = createByteArray( 0x04, 0x0c, 0x2f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x70, 0x61, 0x74, 0x68); Assertions.assertArrayEquals(expected, buf.toByteArray()); } @Test public void testLiteralHeaderNeverIndexedEncodingRFC7541Examples() throws Exception { final OutboundDynamicTable dynamicTable = new OutboundDynamicTable(); final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII); final ByteArrayBuffer buf = new ByteArrayBuffer(128); final Header header = new BasicHeader("password", "secret", true); encoder.encodeLiteralHeader(buf, null, header, HPackRepresentation.NEVER_INDEXED, false); final byte[] expected = createByteArray( 0x10, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74); Assertions.assertArrayEquals(expected, buf.toByteArray()); } @Test public void testIndexedHeaderEncodingRFC7541Examples() throws Exception { final OutboundDynamicTable dynamicTable = new OutboundDynamicTable(); final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII); final ByteArrayBuffer buf = new ByteArrayBuffer(128); encoder.encodeIndex(buf, 2); final byte[] expected = createByteArray(0x82); Assertions.assertArrayEquals(expected, buf.toByteArray()); } @Test public void testRequestEncodingWithoutHuffmanRFC7541Examples() throws Exception { final OutboundDynamicTable dynamicTable = new OutboundDynamicTable(); final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII); final ByteArrayBuffer buf = new ByteArrayBuffer(256); final List
headers1 = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":path", "/"), new BasicHeader(":authority", "www.example.com")); encoder.encodeHeaders(buf, headers1, false, false); final byte[] expected1 = createByteArray( 0x82, 0x86, 0x84, 0x41, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d); Assertions.assertArrayEquals(expected1, buf.toByteArray()); Assertions.assertEquals(1, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(0)); Assertions.assertEquals(57, dynamicTable.getCurrentSize()); final List
headers2 = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":path", "/"), new BasicHeader(":authority", "www.example.com"), new BasicHeader("cache-control", "no-cache")); buf.clear(); encoder.encodeHeaders(buf, headers2, false, false); final byte[] expected2 = createByteArray( 0x82, 0x86, 0x84, 0xbe, 0x58, 0x08, 0x6e, 0x6f, 0x2d, 0x63, 0x61, 0x63, 0x68, 0x65); Assertions.assertArrayEquals(expected2, buf.toByteArray()); Assertions.assertEquals(2, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(1)); Assertions.assertEquals(110, dynamicTable.getCurrentSize()); final List
headers3 = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "https"), new BasicHeader(":path", "/index.html"), new BasicHeader(":authority", "www.example.com"), new BasicHeader("custom-key", "custom-value")); buf.clear(); encoder.encodeHeaders(buf, headers3, false, false); final byte[] expected3 = createByteArray( 0x82, 0x87, 0x85, 0xbf, 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65); Assertions.assertArrayEquals(expected3, buf.toByteArray()); Assertions.assertEquals(3, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(2)); Assertions.assertEquals(164, dynamicTable.getCurrentSize()); } @Test public void testRequestEncodingWithHuffmanRFC7541Examples() throws Exception { final OutboundDynamicTable dynamicTable = new OutboundDynamicTable(); final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII); final ByteArrayBuffer buf = new ByteArrayBuffer(256); final List
headers1 = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":path", "/"), new BasicHeader(":authority", "www.example.com")); encoder.encodeHeaders(buf, headers1, false, true); final byte[] expected1 = createByteArray( 0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff); Assertions.assertArrayEquals(expected1, buf.toByteArray()); Assertions.assertEquals(1, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(0)); Assertions.assertEquals(57, dynamicTable.getCurrentSize()); final List
headers2 = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":path", "/"), new BasicHeader(":authority", "www.example.com"), new BasicHeader("cache-control", "no-cache")); buf.clear(); encoder.encodeHeaders(buf, headers2, false, true); final byte[] expected2 = createByteArray( 0x82, 0x86, 0x84, 0xbe, 0x58, 0x86, 0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf); Assertions.assertArrayEquals(expected2, buf.toByteArray()); Assertions.assertEquals(2, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(1)); Assertions.assertEquals(110, dynamicTable.getCurrentSize()); final List
headers3 = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "https"), new BasicHeader(":path", "/index.html"), new BasicHeader(":authority", "www.example.com"), new BasicHeader("custom-key", "custom-value")); buf.clear(); encoder.encodeHeaders(buf, headers3, false, true); final byte[] expected3 = createByteArray( 0x82, 0x87, 0x85, 0xbf, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, 0x89, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, 0xbf); Assertions.assertArrayEquals(expected3, buf.toByteArray()); Assertions.assertEquals(3, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(2)); Assertions.assertEquals(164, dynamicTable.getCurrentSize()); } @Test public void testResponseEncodingWithoutHuffmanRFC7541Examples() throws Exception { final OutboundDynamicTable dynamicTable = new OutboundDynamicTable(); dynamicTable.setMaxSize(256); final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII); final ByteArrayBuffer buf = new ByteArrayBuffer(256); final List
headers1 = Arrays.asList( new BasicHeader(":status", "302"), new BasicHeader("cache-control", "private"), new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), new BasicHeader("location", "https://www.example.com")); encoder.encodeHeaders(buf, headers1, false, false); final byte[] expected1 = createByteArray( 0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x31, 0x20, 0x47, 0x4d, 0x54, 0x6e, 0x17, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d); Assertions.assertArrayEquals(expected1, buf.toByteArray()); Assertions.assertEquals(4, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(2)); assertHeaderEquals(new BasicHeader(":status", "302"), dynamicTable.getDynamicEntry(3)); Assertions.assertEquals(222, dynamicTable.getCurrentSize()); final List
headers2 = Arrays.asList( new BasicHeader(":status", "307"), new BasicHeader("cache-control", "private"), new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), new BasicHeader("location", "https://www.example.com")); buf.clear(); encoder.encodeHeaders(buf, headers2, false, false); final byte[] expected2 = createByteArray( 0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf); Assertions.assertArrayEquals(expected2, buf.toByteArray()); Assertions.assertEquals(4, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader(":status", "307"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(2)); assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(3)); Assertions.assertEquals(222, dynamicTable.getCurrentSize()); final List
headers3 = Arrays.asList( new BasicHeader(":status", "200"), new BasicHeader("cache-control", "private"), new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), new BasicHeader("location", "https://www.example.com"), new BasicHeader("content-encoding", "gzip"), new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1")); buf.clear(); encoder.encodeHeaders(buf, headers3, false, false); final byte[] expected3 = createByteArray( 0x88, 0xc1, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x32, 0x20, 0x47, 0x4d, 0x54, 0xc0, 0x5a, 0x04, 0x67, 0x7a, 0x69, 0x70, 0x77, 0x38, 0x66, 0x6f, 0x6f, 0x3d, 0x41, 0x53, 0x44, 0x4a, 0x4b, 0x48, 0x51, 0x4b, 0x42, 0x5a, 0x58, 0x4f, 0x51, 0x57, 0x45, 0x4f, 0x50, 0x49, 0x55, 0x41, 0x58, 0x51, 0x57, 0x45, 0x4f, 0x49, 0x55, 0x3b, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, 0x3d, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31); Assertions.assertArrayEquals(expected3, buf.toByteArray()); Assertions.assertEquals(3, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), dynamicTable.getDynamicEntry(2)); Assertions.assertEquals(215, dynamicTable.getCurrentSize()); } @Test public void testResponseEncodingWithHuffmanRFC7541Examples() throws Exception { final OutboundDynamicTable dynamicTable = new OutboundDynamicTable(); dynamicTable.setMaxSize(256); final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII); final ByteArrayBuffer buf = new ByteArrayBuffer(256); final List
headers1 = Arrays.asList( new BasicHeader(":status", "302"), new BasicHeader("cache-control", "private"), new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), new BasicHeader("location", "https://www.example.com")); encoder.encodeHeaders(buf, headers1, false, true); final byte[] expected1 = createByteArray( 0x48, 0x82, 0x64, 0x02, 0x58, 0x85, 0xae, 0xc3, 0x77, 0x1a, 0x4b, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x82, 0xa6, 0x2d, 0x1b, 0xff, 0x6e, 0x91, 0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, 0x8f, 0x0b, 0x97, 0xc8, 0xe9, 0xae, 0x82, 0xae, 0x43, 0xd3); Assertions.assertArrayEquals(expected1, buf.toByteArray()); Assertions.assertEquals(4, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(2)); assertHeaderEquals(new BasicHeader(":status", "302"), dynamicTable.getDynamicEntry(3)); Assertions.assertEquals(222, dynamicTable.getCurrentSize()); final List
headers2 = Arrays.asList( new BasicHeader(":status", "307"), new BasicHeader("cache-control", "private"), new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), new BasicHeader("location", "https://www.example.com")); buf.clear(); encoder.encodeHeaders(buf, headers2, false, true); final byte[] expected2 = createByteArray( 0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf); Assertions.assertArrayEquals(expected2, buf.toByteArray()); Assertions.assertEquals(4, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader(":status", "307"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(2)); assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(3)); Assertions.assertEquals(222, dynamicTable.getCurrentSize()); final List
headers3 = Arrays.asList( new BasicHeader(":status", "200"), new BasicHeader("cache-control", "private"), new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), new BasicHeader("location", "https://www.example.com"), new BasicHeader("content-encoding", "gzip"), new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1")); buf.clear(); encoder.encodeHeaders(buf, headers3, false, true); final byte[] expected3 = createByteArray( 0x88, 0xc1, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x84, 0xa6, 0x2d, 0x1b, 0xff, 0xc0, 0x5a, 0x83, 0x9b, 0xd9, 0xab, 0x77, 0xad, 0x94, 0xe7, 0x82, 0x1d, 0xd7, 0xf2, 0xe6, 0xc7, 0xb3, 0x35, 0xdf, 0xdf, 0xcd, 0x5b, 0x39, 0x60, 0xd5, 0xaf, 0x27, 0x08, 0x7f, 0x36, 0x72, 0xc1, 0xab, 0x27, 0x0f, 0xb5, 0x29, 0x1f, 0x95, 0x87, 0x31, 0x60, 0x65, 0xc0, 0x03, 0xed, 0x4e, 0xe5, 0xb1, 0x06, 0x3d, 0x50, 0x07); Assertions.assertArrayEquals(expected3, buf.toByteArray()); Assertions.assertEquals(3, dynamicTable.dynamicLength()); assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), dynamicTable.getDynamicEntry(0)); assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), dynamicTable.getDynamicEntry(1)); assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), dynamicTable.getDynamicEntry(2)); Assertions.assertEquals(215, dynamicTable.getCurrentSize()); } @Test public void testHeaderEntrySizeNonAscii() throws Exception { final ByteArrayBuffer buffer = new ByteArrayBuffer(128); final Header header = new BasicHeader("hello", constructHelloString(SWISS_GERMAN_HELLO, 1)); final OutboundDynamicTable outboundTable1 = new OutboundDynamicTable(); final HPackEncoder encoder1 = new HPackEncoder(outboundTable1, StandardCharsets.ISO_8859_1); final InboundDynamicTable inboundTable1 = new InboundDynamicTable(); final HPackDecoder decoder1 = new HPackDecoder(inboundTable1, StandardCharsets.ISO_8859_1); encoder1.setMaxTableSize(48); decoder1.setMaxTableSize(48); encoder1.encodeHeader(buffer, header); assertHeaderEquals(header, decoder1.decodeHeader(wrap(buffer))); Assertions.assertEquals(1, outboundTable1.dynamicLength()); Assertions.assertEquals(1, inboundTable1.dynamicLength()); assertHeaderEquals(header, outboundTable1.getDynamicEntry(0)); assertHeaderEquals(header, inboundTable1.getDynamicEntry(0)); buffer.clear(); final OutboundDynamicTable outboundTable2 = new OutboundDynamicTable(); final HPackEncoder encoder2 = new HPackEncoder(outboundTable2, StandardCharsets.UTF_8); final InboundDynamicTable inboundTable2 = new InboundDynamicTable(); final HPackDecoder decoder2 = new HPackDecoder(inboundTable2, StandardCharsets.UTF_8); encoder2.setMaxTableSize(48); decoder2.setMaxTableSize(48); encoder2.encodeHeader(buffer, header); assertHeaderEquals(header, decoder2.decodeHeader(wrap(buffer))); Assertions.assertEquals(0, outboundTable2.dynamicLength()); Assertions.assertEquals(0, inboundTable2.dynamicLength()); } @Test public void testHeaderSizeLimit() throws Exception { final HPackEncoder encoder = new HPackEncoder(StandardCharsets.US_ASCII); final HPackDecoder decoder = new HPackDecoder(StandardCharsets.US_ASCII); final ByteArrayBuffer buf = new ByteArrayBuffer(128); encoder.encodeHeaders(buf, Arrays.asList( new BasicHeader("regular-header", "blah"), new BasicHeader("big-f-header", "12345678901234567890123456789012345678901234567890" + "123456789012345678901234567890123456789012345678901234567890")), false); assertThat(decoder.decodeHeaders(wrap(buf)).size(), CoreMatchers.equalTo(2)); decoder.setMaxListSize(1000000); assertThat(decoder.decodeHeaders(wrap(buf)).size(), CoreMatchers.equalTo(2)); decoder.setMaxListSize(200); Assertions.assertThrows(HeaderListConstraintException.class, () -> decoder.decodeHeaders(wrap(buf))); } @Test public void testHeaderEmptyASCII() throws Exception { final HPackEncoder encoder = new HPackEncoder(StandardCharsets.US_ASCII); final HPackDecoder decoder = new HPackDecoder(StandardCharsets.US_ASCII); final ByteArrayBuffer buf = new ByteArrayBuffer(128); final Header header = new BasicHeader("empty-header", ""); encoder.encodeHeader(buf, header); assertHeaderEquals(header, decoder.decodeHeader(wrap(buf))); } @Test public void testHeaderEmptyUTF8() throws Exception { final HPackEncoder encoder = new HPackEncoder(StandardCharsets.UTF_8); final HPackDecoder decoder = new HPackDecoder(StandardCharsets.UTF_8); final ByteArrayBuffer buf = new ByteArrayBuffer(128); final Header header = new BasicHeader("empty-header", ""); encoder.encodeHeader(buf, header); assertHeaderEquals(header, decoder.decodeHeader(wrap(buf))); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/hpack/TestFifoBuffer.java0100664 0000000 0000000 00000013216 14403631147 026016 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.hpack; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestFifoBuffer { @Test public void testAddRemoveCycle() throws Exception { final FifoBuffer fifoBuffer = new FifoBuffer(5); final HPackHeader h1 = new HPackHeader("h", "1"); final HPackHeader h2 = new HPackHeader("h", "2"); final HPackHeader h3 = new HPackHeader("h", "3"); final HPackHeader h4 = new HPackHeader("h", "4"); for (int i = 0; i < 20; i++) { Assertions.assertEquals(0, fifoBuffer.size()); Assertions.assertSame(null, fifoBuffer.getFirst()); Assertions.assertSame(null, fifoBuffer.getLast()); fifoBuffer.addFirst(h1); Assertions.assertSame(h1, fifoBuffer.getFirst()); Assertions.assertSame(h1, fifoBuffer.getLast()); Assertions.assertEquals(1, fifoBuffer.size()); fifoBuffer.addFirst(h2); Assertions.assertEquals(2, fifoBuffer.size()); Assertions.assertSame(h2, fifoBuffer.getFirst()); Assertions.assertSame(h1, fifoBuffer.getLast()); fifoBuffer.addFirst(h3); Assertions.assertEquals(3, fifoBuffer.size()); Assertions.assertSame(h3, fifoBuffer.getFirst()); Assertions.assertSame(h1, fifoBuffer.getLast()); fifoBuffer.addFirst(h4); Assertions.assertEquals(4, fifoBuffer.size()); Assertions.assertSame(h4, fifoBuffer.getFirst()); Assertions.assertSame(h1, fifoBuffer.getLast()); Assertions.assertSame(h4, fifoBuffer.get(0)); Assertions.assertSame(h3, fifoBuffer.get(1)); Assertions.assertSame(h2, fifoBuffer.get(2)); Assertions.assertSame(h1, fifoBuffer.get(3)); fifoBuffer.removeLast(); Assertions.assertEquals(3, fifoBuffer.size()); Assertions.assertSame(h4, fifoBuffer.getFirst()); Assertions.assertSame(h2, fifoBuffer.getLast()); fifoBuffer.removeLast(); Assertions.assertEquals(2, fifoBuffer.size()); Assertions.assertSame(h4, fifoBuffer.getFirst()); Assertions.assertSame(h3, fifoBuffer.getLast()); fifoBuffer.removeLast(); Assertions.assertEquals(1, fifoBuffer.size()); Assertions.assertSame(h4, fifoBuffer.getFirst()); Assertions.assertSame(h4, fifoBuffer.getLast()); fifoBuffer.removeLast(); Assertions.assertEquals(0, fifoBuffer.size()); Assertions.assertSame(null, fifoBuffer.getFirst()); Assertions.assertSame(null, fifoBuffer.getLast()); } } @Test public void testExpand() throws Exception { final HPackHeader h1 = new HPackHeader("h", "1"); final HPackHeader h2 = new HPackHeader("h", "2"); final HPackHeader h3 = new HPackHeader("h", "3"); final HPackHeader h4 = new HPackHeader("h", "4"); for (int i = 0; i < 10; i++) { final FifoBuffer fifoBuffer = new FifoBuffer(1); for (int n = 0; n < i; n++) { fifoBuffer.addFirst(h1); fifoBuffer.removeLast(); } Assertions.assertEquals(0, fifoBuffer.size()); Assertions.assertSame(null, fifoBuffer.getFirst()); Assertions.assertSame(null, fifoBuffer.getLast()); fifoBuffer.addFirst(h1); Assertions.assertSame(h1, fifoBuffer.getFirst()); Assertions.assertSame(h1, fifoBuffer.getLast()); Assertions.assertEquals(1, fifoBuffer.size()); fifoBuffer.addFirst(h2); Assertions.assertEquals(2, fifoBuffer.size()); Assertions.assertSame(h2, fifoBuffer.getFirst()); Assertions.assertSame(h1, fifoBuffer.getLast()); fifoBuffer.addFirst(h3); Assertions.assertEquals(3, fifoBuffer.size()); Assertions.assertSame(h3, fifoBuffer.getFirst()); Assertions.assertSame(h1, fifoBuffer.getLast()); fifoBuffer.addFirst(h4); Assertions.assertEquals(4, fifoBuffer.size()); Assertions.assertSame(h4, fifoBuffer.getFirst()); Assertions.assertSame(h1, fifoBuffer.getLast()); Assertions.assertSame(h4, fifoBuffer.get(0)); Assertions.assertSame(h3, fifoBuffer.get(1)); Assertions.assertSame(h2, fifoBuffer.get(2)); Assertions.assertSame(h1, fifoBuffer.get(3)); } } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/0040775 0000000 0000000 00000000000 14435411677 023037 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2FullDuplexClientExample.java0100664 0000000 0000000 00000020232 14403631147 030616 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.examples; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.Timeout; /** * Example of full-duplex, streaming HTTP message exchanges with an asynchronous HTTP/2 requester. */ public class H2FullDuplexClientExample { public static void main(final String[] args) throws Exception { final IOReactorConfig ioReactorConfig = IOReactorConfig.custom() .setSoTimeout(5, TimeUnit.SECONDS) .build(); // Create and start requester final H2Config h2Config = H2Config.custom() .setPushEnabled(false) .setMaxConcurrentStreams(100) .build(); final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap() .setIOReactorConfig(ioReactorConfig) .setH2Config(h2Config) .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2) .setStreamListener(new H2StreamListener() { @Override public void onHeaderInput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") << " + headers.get(i)); } } @Override public void onHeaderOutput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") >> " + headers.get(i)); } } @Override public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } @Override public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP requester shutting down"); requester.close(CloseMode.GRACEFUL); })); requester.start(); final URI requestUri = new URI("http://nghttp2.org/httpbin/post"); final AsyncRequestProducer requestProducer = AsyncRequestBuilder.post(requestUri) .setEntity("stuff") .build(); final BasicResponseConsumer responseConsumer = new BasicResponseConsumer<>( new StringAsyncEntityConsumer()); final CountDownLatch latch = new CountDownLatch(1); requester.execute(new AsyncClientExchangeHandler() { @Override public void releaseResources() { requestProducer.releaseResources(); responseConsumer.releaseResources(); latch.countDown(); } @Override public void cancel() { System.out.println(requestUri + " cancelled"); } @Override public void failed(final Exception cause) { System.out.println(requestUri + "->" + cause); } @Override public void produceRequest(final RequestChannel channel, final HttpContext httpContext) throws HttpException, IOException { requestProducer.sendRequest(channel, httpContext); } @Override public int available() { return requestProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { requestProducer.produce(channel); } @Override public void consumeInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException { System.out.println(requestUri + "->" + response.getCode()); } @Override public void consumeResponse(final HttpResponse response, final EntityDetails entityDetails, final HttpContext httpContext) throws HttpException, IOException { System.out.println(requestUri + "->" + response.getCode()); responseConsumer.consumeResponse(response, entityDetails, httpContext, null); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { responseConsumer.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { responseConsumer.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { responseConsumer.streamEnd(trailers); } }, Timeout.ofSeconds(30), HttpCoreContext.create()); latch.await(); System.out.println("Shutting down I/O reactor"); requester.initiateShutdown(); } } ././@LongLink0100644 0000000 0000000 00000000146 14403631147 011637 Lustar 0000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2ConscriptRequestExecutionExample.javahttpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2ConscriptRequestExecutionExample.jav0100664 0000000 0000000 00000016152 14403631147 032441 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.examples; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import javax.net.ssl.SSLContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap; import org.apache.hc.core5.http2.ssl.ConscryptClientTlsStrategy; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.Timeout; import org.conscrypt.Conscrypt; /** * This example demonstrates how to execute HTTP/2 requests over TLS connections * with Java 1.7 or Java 1.8 and Conscrypt TLS library */ public class H2ConscriptRequestExecutionExample { public static void main(final String[] args) throws Exception { // Create and start requester final H2Config h2Config = H2Config.custom() .setPushEnabled(false) .build(); final SSLContext sslContext = SSLContexts.custom() .setProvider(Conscrypt.newProvider()) .build(); final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap() .setH2Config(h2Config) .setTlsStrategy(new ConscryptClientTlsStrategy(sslContext)) .setStreamListener(new H2StreamListener() { @Override public void onHeaderInput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") << " + headers.get(i)); } } @Override public void onHeaderOutput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") >> " + headers.get(i)); } } @Override public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } @Override public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP requester shutting down"); requester.close(CloseMode.GRACEFUL); })); requester.start(); final HttpHost target = new HttpHost("https", "nghttp2.org", 443); final String[] requestUris = new String[] {"/httpbin/ip", "/httpbin/user-agent", "/httpbin/headers"}; final CountDownLatch latch = new CountDownLatch(requestUris.length); for (final String requestUri: requestUris) { final Future future = requester.connect(target, Timeout.ofDays(5)); final AsyncClientEndpoint clientEndpoint = future.get(); clientEndpoint.execute( AsyncRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), new FutureCallback>() { @Override public void completed(final Message message) { clientEndpoint.releaseAndReuse(); final HttpResponse response = message.getHead(); final String body = message.getBody(); System.out.println(requestUri + "->" + response.getCode() + " " + response.getVersion()); System.out.println(body); latch.countDown(); } @Override public void failed(final Exception ex) { clientEndpoint.releaseAndDiscard(); System.out.println(requestUri + "->" + ex); latch.countDown(); } @Override public void cancelled() { clientEndpoint.releaseAndDiscard(); System.out.println(requestUri + " cancelled"); latch.countDown(); } }); } latch.await(); System.out.println("Shutting down I/O reactor"); requester.initiateShutdown(); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2FullDuplexServerExample.java0100664 0000000 0000000 00000024021 14403631147 030646 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.examples; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2ServerBootstrap; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.util.TimeValue; /** * Example of full-duplex, streaming HTTP message exchanges with an asynchronous embedded HTTP/2 server. */ public class H2FullDuplexServerExample { public static void main(final String[] args) throws Exception { int port = 8080; if (args.length >= 1) { port = Integer.parseInt(args[0]); } final IOReactorConfig config = IOReactorConfig.custom() .setSoTimeout(15, TimeUnit.SECONDS) .setTcpNoDelay(true) .build(); final H2Config h2Config = H2Config.custom() .setPushEnabled(true) .setMaxConcurrentStreams(100) .build(); final HttpAsyncServer server = H2ServerBootstrap.bootstrap() .setIOReactorConfig(config) .setH2Config(h2Config) .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2) .setStreamListener(new H2StreamListener() { @Override public void onHeaderInput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") << " + headers.get(i)); } } @Override public void onHeaderOutput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") >> " + headers.get(i)); } } @Override public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } @Override public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } }) .register("/echo", () -> new AsyncServerExchangeHandler() { ByteBuffer buffer = ByteBuffer.allocate(2048); CapacityChannel inputCapacityChannel; DataStreamChannel outputDataChannel; boolean endStream; private void ensureCapacity(final int chunk) { if (buffer.remaining() < chunk) { final ByteBuffer oldBuffer = buffer; oldBuffer.flip(); buffer = ByteBuffer.allocate(oldBuffer.remaining() + (chunk > 2048 ? chunk : 2048)); buffer.put(oldBuffer); } } @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); responseChannel.sendResponse(response, entityDetails, context); } @Override public void consume(final ByteBuffer src) throws IOException { if (buffer.position() == 0) { if (outputDataChannel != null) { outputDataChannel.write(src); } } if (src.hasRemaining()) { ensureCapacity(src.remaining()); buffer.put(src); if (outputDataChannel != null) { outputDataChannel.requestOutput(); } } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { if (buffer.hasRemaining()) { capacityChannel.update(buffer.remaining()); inputCapacityChannel = null; } else { inputCapacityChannel = capacityChannel; } } @Override public void streamEnd(final List trailers) throws IOException { endStream = true; if (buffer.position() == 0) { if (outputDataChannel != null) { outputDataChannel.endStream(); } } else { if (outputDataChannel != null) { outputDataChannel.requestOutput(); } } } @Override public int available() { return buffer.position(); } @Override public void produce(final DataStreamChannel channel) throws IOException { outputDataChannel = channel; buffer.flip(); if (buffer.hasRemaining()) { channel.write(buffer); } buffer.compact(); if (buffer.position() == 0 && endStream) { channel.endStream(); } final CapacityChannel capacityChannel = inputCapacityChannel; if (capacityChannel != null && buffer.hasRemaining()) { capacityChannel.update(buffer.remaining()); } } @Override public void failed(final Exception cause) { if (!(cause instanceof SocketException)) { cause.printStackTrace(System.out); } } @Override public void releaseResources() { } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP server shutting down"); server.close(CloseMode.GRACEFUL); })); server.start(); final Future future = server.listen(new InetSocketAddress(port), URIScheme.HTTP); final ListenerEndpoint listenerEndpoint = future.get(); System.out.print("Listening on " + listenerEndpoint.getAddress()); server.awaitShutdown(TimeValue.ofDays(Long.MAX_VALUE)); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2MultiStreamExecutionExample.java0100664 0000000 0000000 00000015744 14403631147 031541 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.examples; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.Timeout; /** * Example of HTTP/2 concurrent request execution using multiple streams. */ public class H2MultiStreamExecutionExample { public static void main(final String[] args) throws Exception { // Create and start requester final IOReactorConfig ioReactorConfig = IOReactorConfig.custom() .setSoTimeout(5, TimeUnit.SECONDS) .build(); final H2Config h2Config = H2Config.custom() .setPushEnabled(false) .setMaxConcurrentStreams(100) .build(); final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap() .setIOReactorConfig(ioReactorConfig) .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2) .setH2Config(h2Config) .setStreamListener(new H2StreamListener() { @Override public void onHeaderInput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") << " + headers.get(i)); } } @Override public void onHeaderOutput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") >> " + headers.get(i)); } } @Override public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } @Override public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP requester shutting down"); requester.close(CloseMode.GRACEFUL); })); requester.start(); final HttpHost target = new HttpHost("nghttp2.org"); final String[] requestUris = new String[] {"/httpbin/ip", "/httpbin/user-agent", "/httpbin/headers"}; final Future future = requester.connect(target, Timeout.ofSeconds(5)); final AsyncClientEndpoint clientEndpoint = future.get(); final CountDownLatch latch = new CountDownLatch(requestUris.length); for (final String requestUri: requestUris) { clientEndpoint.execute( AsyncRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), new FutureCallback>() { @Override public void completed(final Message message) { latch.countDown(); final HttpResponse response = message.getHead(); final String body = message.getBody(); System.out.println(requestUri + "->" + response.getCode()); System.out.println(body); } @Override public void failed(final Exception ex) { latch.countDown(); System.out.println(requestUri + "->" + ex); } @Override public void cancelled() { latch.countDown(); System.out.println(requestUri + " cancelled"); } }); } latch.await(); // Manually release client endpoint when done !!! clientEndpoint.releaseAndDiscard(); System.out.println("Shutting down I/O reactor"); requester.initiateShutdown(); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2FileServerExample.java0100664 0000000 0000000 00000023441 14403631147 027446 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.examples; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.nio.entity.DiscardingEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncResponseBuilder; import org.apache.hc.core5.http.nio.support.BasicRequestConsumer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2ServerBootstrap; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.TimeValue; /** * Example of asynchronous embedded HTTP/2 file server. */ public class H2FileServerExample { public static void main(final String[] args) throws Exception { if (args.length < 1) { System.err.println("Please specify document root directory"); System.exit(1); } // Document root directory final File docRoot = new File(args[0]); int port = 8080; if (args.length >= 2) { port = Integer.parseInt(args[1]); } final IOReactorConfig config = IOReactorConfig.custom() .setSoTimeout(15, TimeUnit.SECONDS) .setTcpNoDelay(true) .build(); final HttpAsyncServer server = H2ServerBootstrap.bootstrap() .setIOReactorConfig(config) .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2) .setStreamListener(new H2StreamListener() { @Override public void onHeaderInput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") << " + headers.get(i)); } } @Override public void onHeaderOutput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") >> " + headers.get(i)); } } @Override public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } @Override public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } }) .register("*", new AsyncServerRequestHandler>() { @Override public AsyncRequestConsumer> prepare( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) throws HttpException { return new BasicRequestConsumer<>(entityDetails != null ? new DiscardingEntityConsumer<>() : null); } @Override public void handle( final Message message, final ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { final HttpRequest request = message.getHead(); final URI requestUri; try { requestUri = request.getUri(); } catch (final URISyntaxException ex) { throw new ProtocolException(ex.getMessage(), ex); } final String path = requestUri.getPath(); final File file = new File(docRoot, path); if (!file.exists()) { System.out.println("File " + file.getPath() + " not found"); responseTrigger.submitResponse( AsyncResponseBuilder.create(HttpStatus.SC_NOT_FOUND) .setEntity("

File" + file.getPath() + " not found

", ContentType.TEXT_HTML) .build(), context); } else if (!file.canRead() || file.isDirectory()) { System.out.println("Cannot read file " + file.getPath()); responseTrigger.submitResponse( AsyncResponseBuilder.create(HttpStatus.SC_FORBIDDEN) .setEntity("

Access denied

", ContentType.TEXT_HTML) .build(), context); } else { final ContentType contentType; final String filename = TextUtils.toLowerCase(file.getName()); if (filename.endsWith(".txt")) { contentType = ContentType.TEXT_PLAIN; } else if (filename.endsWith(".html") || filename.endsWith(".htm")) { contentType = ContentType.TEXT_HTML; } else if (filename.endsWith(".xml")) { contentType = ContentType.TEXT_XML; } else { contentType = ContentType.DEFAULT_BINARY; } final HttpCoreContext coreContext = HttpCoreContext.adapt(context); final EndpointDetails endpoint = coreContext.getEndpointDetails(); System.out.println(endpoint + ": serving file " + file.getPath()); responseTrigger.submitResponse( AsyncResponseBuilder.create(HttpStatus.SC_OK) .setEntity(AsyncEntityProducers.create(file, contentType)) .build(), context); } } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP server shutting down"); server.close(CloseMode.GRACEFUL); })); server.start(); final Future future = server.listen(new InetSocketAddress(port), URIScheme.HTTP); final ListenerEndpoint listenerEndpoint = future.get(); System.out.print("Listening on " + listenerEndpoint.getAddress()); server.awaitShutdown(TimeValue.ofDays(Long.MAX_VALUE)); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2TlsAlpnRequestExecutionExample.java0100664 0000000 0000000 00000016563 14403631147 032221 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.examples; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap; import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.Timeout; /** * This example demonstrates how to execute HTTP/2 requests over TLS connections. *

* It requires Java runtime with ALPN protocol support (such as Oracle JRE 9 or newer). */ public class H2TlsAlpnRequestExecutionExample { public static void main(final String[] args) throws Exception { // Create and start requester final H2Config h2Config = H2Config.custom() .setPushEnabled(false) .build(); final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap() .setH2Config(h2Config) .setTlsStrategy(new H2ClientTlsStrategy(SSLContexts.createSystemDefault(), (endpoint, sslEngine) -> { // IMPORTANT uncomment the following line when running Java 9 or older // in order to avoid the illegal reflective access operation warning // ==== // return new TlsDetails(sslEngine.getSession(), sslEngine.getApplicationProtocol()); // ==== return null; })) .setStreamListener(new H2StreamListener() { @Override public void onHeaderInput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") << " + headers.get(i)); } } @Override public void onHeaderOutput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") >> " + headers.get(i)); } } @Override public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } @Override public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP requester shutting down"); requester.close(CloseMode.GRACEFUL); })); requester.start(); final HttpHost target = new HttpHost("https", "nghttp2.org", 443); final String[] requestUris = new String[] {"/httpbin/ip", "/httpbin/user-agent", "/httpbin/headers"}; final CountDownLatch latch = new CountDownLatch(requestUris.length); for (final String requestUri: requestUris) { final Future future = requester.connect(target, Timeout.ofSeconds(5)); final AsyncClientEndpoint clientEndpoint = future.get(); clientEndpoint.execute( AsyncRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), new FutureCallback>() { @Override public void completed(final Message message) { clientEndpoint.releaseAndReuse(); final HttpResponse response = message.getHead(); final String body = message.getBody(); System.out.println(requestUri + "->" + response.getCode() + " " + response.getVersion()); System.out.println(body); latch.countDown(); } @Override public void failed(final Exception ex) { clientEndpoint.releaseAndDiscard(); System.out.println(requestUri + "->" + ex); latch.countDown(); } @Override public void cancelled() { clientEndpoint.releaseAndDiscard(); System.out.println(requestUri + " cancelled"); latch.countDown(); } }); } latch.await(); System.out.println("Shutting down I/O reactor"); requester.initiateShutdown(); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2GreetingServer.java0100664 0000000 0000000 00000016775 14435411677 027044 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.examples; import java.io.IOException; import java.net.InetSocketAddress; import java.time.Instant; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.nio.entity.DiscardingEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AbstractServerExchangeHandler; import org.apache.hc.core5.http.nio.support.BasicRequestConsumer; import org.apache.hc.core5.http.nio.support.BasicResponseProducer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2ServerBootstrap; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.net.WWWFormCodec; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.util.TimeValue; /** * Example HTTP2 server that reads an entity body and responds back with a greeting. * *

 * {@code
 * $ curl  -id name=bob localhost:8080
 * HTTP/1.1 200 OK
 * Date: Sat, 25 May 2019 03:44:49 GMT
 * Server: Apache-HttpCore/5.0-beta8-SNAPSHOT (Java/1.8.0_202)
 * Transfer-Encoding: chunked
 * Content-Type: text/plain; charset=ISO-8859-1
 *
 * Hello bob
 * }
*

* This examples uses a {@link AbstractServerExchangeHandler} for the basic request / response processing cycle. */ public class H2GreetingServer { public static void main(final String[] args) throws ExecutionException, InterruptedException { int port = 8080; if (args.length >= 1) { port = Integer.parseInt(args[0]); } final HttpAsyncServer server = H2ServerBootstrap.bootstrap() .setH2Config(H2Config.DEFAULT) .setIOReactorConfig(IOReactorConfig.DEFAULT) .setVersionPolicy(HttpVersionPolicy.NEGOTIATE) // fallback to HTTP/1 as needed // wildcard path matcher: .register("*", CustomServerExchangeHandler::new) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP server shutting down"); server.close(CloseMode.GRACEFUL); })); server.start(); final Future future = server.listen(new InetSocketAddress(port), URIScheme.HTTP); final ListenerEndpoint listenerEndpoint = future.get(); System.out.println("Listening on " + listenerEndpoint.getAddress()); server.awaitShutdown(TimeValue.ofDays(Long.MAX_VALUE)); } static class CustomServerExchangeHandler extends AbstractServerExchangeHandler> { @Override protected AsyncRequestConsumer> supplyConsumer( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) { // if there's no body don't try to parse entity: AsyncEntityConsumer entityConsumer = new DiscardingEntityConsumer<>(); if (entityDetails != null) { entityConsumer = new StringAsyncEntityConsumer(); } //noinspection unchecked return new BasicRequestConsumer<>(entityConsumer); } @Override protected void handle(final Message requestMessage, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { final HttpCoreContext coreContext = HttpCoreContext.adapt(context); final EndpointDetails endpoint = coreContext.getEndpointDetails(); final HttpRequest req = requestMessage.getHead(); final String httpEntity = requestMessage.getBody(); // generic success response: final HttpResponse resp = new BasicHttpResponse(200); // recording the request System.out.printf("[%s] %s %s %s%n", Instant.now(), endpoint.getRemoteAddress(), req.getMethod(), req.getPath()); // Request without an entity - GET/HEAD/DELETE if (httpEntity == null) { responseTrigger.submitResponse( new BasicResponseProducer(resp), context); return; } // Request with an entity - POST/PUT final Header cth = req.getHeader(HttpHeaders.CONTENT_TYPE); final ContentType contentType = cth != null ? ContentType.parse(cth.getValue()) : null; String name = "stranger"; if (contentType != null && contentType.isSameMimeType(ContentType.APPLICATION_FORM_URLENCODED)) { // decoding the form entity into key/value pairs: final List args = WWWFormCodec.parse(httpEntity, contentType.getCharset()); if (!args.isEmpty()) { name = args.get(0).getValue(); } } // composing greeting: final String greeting = String.format("Hello %s\n", name); responseTrigger.submitResponse( new BasicResponseProducer(resp, AsyncEntityProducers.create(greeting)), context); } } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2ViaHttp1ProxyExecutionExample.java0100664 0000000 0000000 00000025742 14403631147 031774 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.examples; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.concurrent.FutureContribution; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.DiscardingEntityConsumer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.ssl.TlsUpgradeCapable; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Timeout; /** * Example of asynchronous HTTP/2 request execution via a HTTP/1.1 proxy. */ public class H2ViaHttp1ProxyExecutionExample { public static void main(final String[] args) throws Exception { // Create and start requester final H2Config h2Config = H2Config.custom() .setPushEnabled(false) .build(); final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap() .setH2Config(h2Config) .setVersionPolicy(HttpVersionPolicy.NEGOTIATE) .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { System.out.println(connection.getRemoteAddress() + " " + new RequestLine(request)); } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { System.out.println(connection.getRemoteAddress() + " " + new StatusLine(response)); } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (keepAlive) { System.out.println(connection.getRemoteAddress() + " exchange completed (connection kept alive)"); } else { System.out.println(connection.getRemoteAddress() + " exchange completed (connection closed)"); } } }) .setStreamListener(new H2StreamListener() { @Override public void onHeaderInput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") << " + headers.get(i)); } } @Override public void onHeaderOutput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") >> " + headers.get(i)); } } @Override public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } @Override public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP requester shutting down"); requester.close(CloseMode.GRACEFUL); })); requester.start(); final HttpHost proxy = new HttpHost("localhost", 8888); final HttpHost target = new HttpHost("https", "nghttp2.org"); final ComplexFuture tunnelFuture = new ComplexFuture<>(null); tunnelFuture.setDependency(requester.connect( proxy, Timeout.ofSeconds(30), null, new FutureContribution(tunnelFuture) { @Override public void completed(final AsyncClientEndpoint endpoint) { if (endpoint instanceof TlsUpgradeCapable) { final HttpRequest connect = new BasicHttpRequest(Method.CONNECT, proxy, target.toHostString()); endpoint.execute( new BasicRequestProducer(connect, null), new BasicResponseConsumer<>(new DiscardingEntityConsumer<>()), new FutureContribution>(tunnelFuture) { @Override public void completed(final Message message) { final HttpResponse response = message.getHead(); if (response.getCode() == HttpStatus.SC_OK) { ((TlsUpgradeCapable) endpoint).tlsUpgrade( target, new FutureContribution(tunnelFuture) { @Override public void completed(final ProtocolIOSession protocolSession) { System.out.println("Tunnel to " + target + " via " + proxy + " established"); tunnelFuture.completed(endpoint); } }); } else { tunnelFuture.failed(new HttpException("Tunnel refused: " + new StatusLine(response))); } } }); } else { tunnelFuture.failed(new IllegalStateException("TLS upgrade not supported")); } } })); final String[] requestUris = new String[] {"/httpbin/ip", "/httpbin/user-agent", "/httpbin/headers"}; final AsyncClientEndpoint endpoint = tunnelFuture.get(1, TimeUnit.MINUTES); try { final CountDownLatch latch = new CountDownLatch(requestUris.length); for (final String requestUri : requestUris) { endpoint.execute( new BasicRequestProducer(Method.GET, target, requestUri), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), new FutureCallback>() { @Override public void completed(final Message message) { final HttpResponse response = message.getHead(); final String body = message.getBody(); System.out.println(requestUri + "->" + response.getCode()); System.out.println(body); latch.countDown(); } @Override public void failed(final Exception ex) { System.out.println(requestUri + "->" + ex); latch.countDown(); } @Override public void cancelled() { System.out.println(requestUri + " cancelled"); latch.countDown(); } }); } latch.await(); } finally { endpoint.releaseAndDiscard(); } System.out.println("Shutting down I/O reactor"); requester.initiateShutdown(); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2RequestExecutionExample.java0100664 0000000 0000000 00000015307 14403631147 030716 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.examples; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Timeout; /** * Example of HTTP/2 request execution. */ public class H2RequestExecutionExample { public static void main(final String[] args) throws Exception { // Create and start requester final H2Config h2Config = H2Config.custom() .setPushEnabled(false) .build(); final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap() .setH2Config(h2Config) .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2) .setStreamListener(new H2StreamListener() { @Override public void onHeaderInput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") << " + headers.get(i)); } } @Override public void onHeaderOutput(final HttpConnection connection, final int streamId, final List headers) { for (int i = 0; i < headers.size(); i++) { System.out.println(connection.getRemoteAddress() + " (" + streamId + ") >> " + headers.get(i)); } } @Override public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) { } @Override public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } @Override public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP requester shutting down"); requester.close(CloseMode.GRACEFUL); })); requester.start(); final HttpHost target = new HttpHost("nghttp2.org"); final String[] requestUris = new String[] {"/httpbin/ip", "/httpbin/user-agent", "/httpbin/headers"}; final CountDownLatch latch = new CountDownLatch(requestUris.length); for (final String requestUri: requestUris) { final Future future = requester.connect(target, Timeout.ofSeconds(5)); final AsyncClientEndpoint clientEndpoint = future.get(); clientEndpoint.execute( AsyncRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), new FutureCallback>() { @Override public void completed(final Message message) { clientEndpoint.releaseAndReuse(); final HttpResponse response = message.getHead(); final String body = message.getBody(); System.out.println(requestUri + "->" + response.getCode()); System.out.println(body); latch.countDown(); } @Override public void failed(final Exception ex) { clientEndpoint.releaseAndDiscard(); System.out.println(requestUri + "->" + ex); latch.countDown(); } @Override public void cancelled() { clientEndpoint.releaseAndDiscard(); System.out.println(requestUri + " cancelled"); latch.countDown(); } }); } latch.await(); System.out.println("Shutting down I/O reactor"); requester.initiateShutdown(); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/ReadableByteChannelMock.java0100664 0000000 0000000 00000005324 14245617503 026506 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.ReadableByteChannel; public class ReadableByteChannelMock implements ReadableByteChannel { private final byte[][] chunks; private int chunkCount = 0; private ByteBuffer currentChunk; private boolean eof = false; private boolean closed = false; public ReadableByteChannelMock(final byte[]... chunks) { super(); this.chunks = chunks; } private void prepareChunk() { if (this.currentChunk == null || !this.currentChunk.hasRemaining()) { if (this.chunkCount < this.chunks.length) { final byte[] bytes = this.chunks[this.chunkCount]; this.chunkCount++; this.currentChunk = ByteBuffer.wrap(bytes); } else { this.eof = true; } } } @Override public int read(final ByteBuffer dst) throws IOException { if (this.closed) { throw new ClosedChannelException(); } prepareChunk(); if (this.eof) { return -1; } int i = 0; while (dst.hasRemaining() && this.currentChunk.hasRemaining()) { dst.put(this.currentChunk.get()); i++; } return i; } @Override public void close() throws IOException { this.closed = true; } @Override public boolean isOpen() { return !this.closed && !this.eof; } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/ssl/0040775 0000000 0000000 00000000000 14403631147 022011 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/http2/ssl/ConscryptClientTlsStrategyTest.java0100664 0000000 0000000 00000004046 14403631147 031046 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.ssl; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; class ConscryptClientTlsStrategyTest { @Mock private SSLSessionVerifier sslSessionVerifier; @BeforeEach public void prepareMocks() { MockitoAnnotations.openMocks(this); } @Test void test_valid_tls_strategy_creation() { final ConscryptClientTlsStrategy strategy = new ConscryptClientTlsStrategy(); assertNotNull(strategy); } @Test void test_valid_tls_strategy_creation_with_verifier() { final ConscryptClientTlsStrategy strategy = new ConscryptClientTlsStrategy(sslSessionVerifier); assertNotNull(strategy); } }httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/0040775 0000000 0000000 00000000000 14403631147 022151 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/TestDefaultH2ResponseConverter.java0100664 0000000 0000000 00000023305 14403631147 031041 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl; import java.util.Arrays; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDefaultH2ResponseConverter { @Test public void testConvertFromFieldsBasic() throws Exception { final List

headers = Arrays.asList( new BasicHeader(":status", "200"), new BasicHeader("location", "http://www.example.com/"), new BasicHeader("custom123", "value")); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); final HttpResponse response = converter.convert(headers); Assertions.assertNotNull(response ); Assertions.assertEquals(200, response .getCode()); final Header[] allHeaders = response.getHeaders(); Assertions.assertEquals(2, allHeaders.length); Assertions.assertEquals("location", allHeaders[0].getName()); Assertions.assertEquals("http://www.example.com/", allHeaders[0].getValue()); Assertions.assertEquals("custom123", allHeaders[1].getName()); Assertions.assertEquals("value", allHeaders[1].getValue()); } @Test public void testConvertFromFieldsUpperCaseHeaderName() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":Status", "200"), new BasicHeader("location", "http://www.example.com/"), new BasicHeader("custom123", "value")); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Header name ':Status' is invalid (header name contains uppercase characters)"); } @Test public void testConvertFromFieldsInvalidStatusCode() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":status", "boom"), new BasicHeader("location", "http://www.example.com/"), new BasicHeader("custom123", "value")); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers)); } @Test public void testConvertFromFieldsConnectionHeader() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":status", "200"), new BasicHeader("location", "http://www.example.com/"), new BasicHeader("connection", "keep-alive")); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Header 'connection: keep-alive' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsKeepAliveHeader() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":status", "200"), new BasicHeader("location", "http://www.example.com/"), new BasicHeader("keep-alive", "timeout=5, max=1000")); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Header 'keep-alive: timeout=5, max=1000' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsTransferEncodingHeader() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":status", "200"), new BasicHeader("location", "http://www.example.com/"), new BasicHeader("transfer-encoding", "gzip")); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Header 'transfer-encoding: gzip' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsUpgradeHeader() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":status", "200"), new BasicHeader("location", "http://www.example.com/"), new BasicHeader("upgrade", "example/1, foo/2")); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Header 'upgrade: example/1, foo/2' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsMissingStatus() throws Exception { final List
headers = Arrays.asList( new BasicHeader("location", "http://www.example.com/"), new BasicHeader("custom", "value")); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Mandatory response header ':status' not found"); } @Test public void testConvertFromFieldsUnknownPseudoHeader() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":status", "200"), new BasicHeader(":custom", "200"), new BasicHeader("location", "http://www.example.com/"), new BasicHeader("custom1", "value")); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Unsupported response header ':custom'"); } @Test public void testConvertFromFieldsMultipleStatus() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":status", "200"), new BasicHeader(":status", "200"), new BasicHeader("location", "http://www.example.com/"), new BasicHeader("custom1", "value")); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Multiple ':status' response headers are illegal"); } @Test public void testConvertFromMessageBasic() throws Exception { final HttpResponse response = new BasicHttpResponse(200); response.addHeader("custom123", "Value"); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); final List
headers = converter.convert(response); Assertions.assertNotNull(headers); Assertions.assertEquals(2, headers.size()); final Header header1 = headers.get(0); Assertions.assertEquals(":status", header1.getName()); Assertions.assertEquals("200", header1.getValue()); final Header header2 = headers.get(1); Assertions.assertEquals("custom123", header2.getName()); Assertions.assertEquals("Value", header2.getValue()); } @Test public void testConvertFromMessageInvalidStatus() throws Exception { final HttpResponse response = new BasicHttpResponse(99); response.addHeader("Custom123", "Value"); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(response), "Response status 99 is invalid"); } @Test public void testConvertFromMessageConnectionHeader() throws Exception { final HttpResponse response = new BasicHttpResponse(200); response.addHeader("Connection", "Keep-Alive"); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(response), "Header 'Connection: Keep-Alive' is illegal for HTTP/2 messages"); } @Test public void testConvertFromMessageInvalidHeader() throws Exception { final HttpResponse response = new BasicHttpResponse(200); response.addHeader(":custom", "stuff"); final DefaultH2ResponseConverter converter = new DefaultH2ResponseConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(response), "Header name ':custom' is invalid"); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/0040775 0000000 0000000 00000000000 14442305435 022737 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestAbstractH2StreamMultiplexer.java0100664 0000000 0000000 00000020307 14403631147 032004 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.command.ExecutableCommand; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.WritableByteChannelMock; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.DefaultFrameFactory; import org.apache.hc.core5.http2.frame.FrameConsts; import org.apache.hc.core5.http2.frame.FrameFactory; import org.apache.hc.core5.http2.frame.FrameType; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.frame.StreamIdGenerator; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; public class TestAbstractH2StreamMultiplexer { @Mock ProtocolIOSession protocolIOSession; @Mock HttpProcessor httpProcessor; @Mock H2StreamListener h2StreamListener; @BeforeEach public void prepareMocks() { MockitoAnnotations.openMocks(this); } static class H2StreamMultiplexerImpl extends AbstractH2StreamMultiplexer { public H2StreamMultiplexerImpl( final ProtocolIOSession ioSession, final FrameFactory frameFactory, final StreamIdGenerator idGenerator, final HttpProcessor httpProcessor, final CharCodingConfig charCodingConfig, final H2Config h2Config, final H2StreamListener streamListener) { super(ioSession, frameFactory, idGenerator, httpProcessor, charCodingConfig, h2Config, streamListener); } @Override void acceptHeaderFrame() throws H2ConnectionException { } @Override void acceptPushRequest() throws H2ConnectionException { } @Override void acceptPushFrame() throws H2ConnectionException { } @Override H2StreamHandler createRemotelyInitiatedStream( final H2StreamChannel channel, final HttpProcessor httpProcessor, final BasicHttpConnectionMetrics connMetrics, final HandlerFactory pushHandlerFactory) throws IOException { return null; } @Override H2StreamHandler createLocallyInitiatedStream( final ExecutableCommand command, final H2StreamChannel channel, final HttpProcessor httpProcessor, final BasicHttpConnectionMetrics connMetrics) throws IOException { return null; } } @Test public void testInputOneFrame() throws Exception { final WritableByteChannelMock writableChannel = new WritableByteChannelMock(1024); final FrameOutputBuffer outbuffer = new FrameOutputBuffer(16 * 1024); final byte[] data = new byte[FrameConsts.MIN_FRAME_SIZE]; for (int i = 0; i < FrameConsts.MIN_FRAME_SIZE; i++) { data[i] = (byte)(i % 16); } final RawFrame frame = new RawFrame(FrameType.DATA.getValue(), 0, 1, ByteBuffer.wrap(data)); outbuffer.write(frame, writableChannel); final byte[] bytes = writableChannel.toByteArray(); final AbstractH2StreamMultiplexer streamMultiplexer = new H2StreamMultiplexerImpl( protocolIOSession, DefaultFrameFactory.INSTANCE, StreamIdGenerator.ODD, httpProcessor, CharCodingConfig.DEFAULT, H2Config.custom() .setMaxFrameSize(FrameConsts.MIN_FRAME_SIZE) .build(), h2StreamListener); Assertions.assertThrows(H2ConnectionException.class, () -> streamMultiplexer.onInput(ByteBuffer.wrap(bytes))); Mockito.verify(h2StreamListener).onFrameInput( Mockito.same(streamMultiplexer), Mockito.eq(1), Mockito.any()); Assertions.assertThrows(H2ConnectionException.class, () -> { int pos = 0; int remaining = bytes.length; while (remaining > 0) { final int chunk = Math.min(2048, remaining); streamMultiplexer.onInput(ByteBuffer.wrap(bytes, pos, chunk)); pos += chunk; remaining -= chunk; } Mockito.verify(h2StreamListener).onFrameInput( Mockito.same(streamMultiplexer), Mockito.eq(1), Mockito.any()); }); } @Test public void testInputMultipleFrames() throws Exception { final WritableByteChannelMock writableChannel = new WritableByteChannelMock(1024); final FrameOutputBuffer outbuffer = new FrameOutputBuffer(16 * 1024); final byte[] data = new byte[FrameConsts.MIN_FRAME_SIZE]; for (int i = 0; i < FrameConsts.MIN_FRAME_SIZE; i++) { data[i] = (byte)(i % 16); } final RawFrame frame1 = new RawFrame(FrameType.DATA.getValue(), 0, 1, ByteBuffer.wrap(data)); outbuffer.write(frame1, writableChannel); final RawFrame frame2 = new RawFrame(FrameType.DATA.getValue(), 0, 1, ByteBuffer.wrap(data)); outbuffer.write(frame2, writableChannel); final byte[] bytes = writableChannel.toByteArray(); final AbstractH2StreamMultiplexer streamMultiplexer = new H2StreamMultiplexerImpl( protocolIOSession, DefaultFrameFactory.INSTANCE, StreamIdGenerator.ODD, httpProcessor, CharCodingConfig.DEFAULT, H2Config.custom() .setMaxFrameSize(FrameConsts.MIN_FRAME_SIZE) .build(), h2StreamListener); Assertions.assertThrows(H2ConnectionException.class, () -> streamMultiplexer.onInput(ByteBuffer.wrap(bytes))); Mockito.verify(h2StreamListener).onFrameInput( Mockito.same(streamMultiplexer), Mockito.eq(1), Mockito.any()); Assertions.assertThrows(H2ConnectionException.class, () -> { int pos = 0; int remaining = bytes.length; while (remaining > 0) { final int chunk = Math.min(4096, remaining); streamMultiplexer.onInput(ByteBuffer.wrap(bytes, pos, chunk)); pos += chunk; remaining -= chunk; } Mockito.verify(h2StreamListener).onFrameInput( Mockito.same(streamMultiplexer), Mockito.eq(1), Mockito.any()); }); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestFrameInOutBuffers.java0100664 0000000 0000000 00000034257 14442305435 030000 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.nio; import java.nio.ByteBuffer; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2CorruptFrameException; import org.apache.hc.core5.http2.ReadableByteChannelMock; import org.apache.hc.core5.http2.WritableByteChannelMock; import org.apache.hc.core5.http2.frame.FrameConsts; import org.apache.hc.core5.http2.frame.FrameFlag; import org.apache.hc.core5.http2.frame.FrameType; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestFrameInOutBuffers { @Test public void testReadWriteFrame() throws Exception { final WritableByteChannelMock writableChannel = new WritableByteChannelMock(1024); final FrameOutputBuffer outbuffer = new FrameOutputBuffer(16 * 1024); final RawFrame frame = new RawFrame(FrameType.DATA.getValue(), 0, 1, ByteBuffer.wrap(new byte[]{1,2,3,4,5})); outbuffer.write(frame, writableChannel); final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final byte[] bytes = writableChannel.toByteArray(); Assertions.assertEquals(FrameConsts.HEAD_LEN + 5, bytes.length); Assertions.assertEquals(1, outbuffer.getMetrics().getFramesTransferred()); Assertions.assertEquals(bytes.length, outbuffer.getMetrics().getBytesTransferred()); final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(bytes); final RawFrame frame2 = inBuffer.read(readableChannel); Assertions.assertEquals(FrameType.DATA.getValue(), frame2.getType()); Assertions.assertEquals(0, frame2.getFlags()); Assertions.assertEquals(1L, frame2.getStreamId()); final ByteBuffer payload2 = frame2.getPayloadContent(); Assertions.assertNotNull(payload2); Assertions.assertEquals(5, payload2.remaining()); Assertions.assertEquals(1, payload2.get()); Assertions.assertEquals(2, payload2.get()); Assertions.assertEquals(3, payload2.get()); Assertions.assertEquals(4, payload2.get()); Assertions.assertEquals(5, payload2.get()); Assertions.assertEquals(-1, readableChannel.read(ByteBuffer.allocate(1024))); Assertions.assertEquals(1, inBuffer.getMetrics().getFramesTransferred()); Assertions.assertEquals(bytes.length, inBuffer.getMetrics().getBytesTransferred()); final RawFrame frame3 = inBuffer.read(ByteBuffer.wrap(bytes), readableChannel); Assertions.assertEquals(FrameType.DATA.getValue(), frame3.getType()); Assertions.assertEquals(0, frame3.getFlags()); Assertions.assertEquals(1L, frame3.getStreamId()); final ByteBuffer payload3 = frame3.getPayloadContent(); Assertions.assertNotNull(payload3); Assertions.assertEquals(5, payload3.remaining()); Assertions.assertEquals(1, payload3.get()); Assertions.assertEquals(2, payload3.get()); Assertions.assertEquals(3, payload3.get()); Assertions.assertEquals(4, payload3.get()); Assertions.assertEquals(5, payload3.get()); Assertions.assertEquals(2, inBuffer.getMetrics().getFramesTransferred()); Assertions.assertEquals(bytes.length * 2, inBuffer.getMetrics().getBytesTransferred()); } @Test public void testPartialFrameWrite() throws Exception { final WritableByteChannelMock writableChannel = new WritableByteChannelMock(1024, FrameConsts.HEAD_LEN + 10); final FrameOutputBuffer outbuffer = new FrameOutputBuffer(16 * 1024); final RawFrame frame = new RawFrame(FrameType.DATA.getValue(), FrameFlag.END_STREAM.getValue(), 5, ByteBuffer.wrap(new byte[]{'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'})); outbuffer.write(frame, writableChannel); Assertions.assertArrayEquals(new byte[] {0,0,16,0,1,0,0,0,5,48,49,50,51,52,53,54,55,56,57}, writableChannel.toByteArray()); Assertions.assertFalse(outbuffer.isEmpty()); outbuffer.flush(writableChannel); Assertions.assertArrayEquals(new byte[] {0,0,16,0,1,0,0,0,5,48,49,50,51,52,53,54,55,56,57}, writableChannel.toByteArray()); writableChannel.flush(); outbuffer.flush(writableChannel); Assertions.assertArrayEquals(new byte[] {0,0,16,0,1,0,0,0,5,48,49,50,51,52,53,54,55,56,57,97,98,99,100,101,102}, writableChannel.toByteArray()); } @Test public void testReadFrameMultiple() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock( new byte[] { 0,0,10,0,8,0,0,0,8,4,0,1,2,3,4,0,0,0,0, 0,0,10,0,9,0,0,0,8,4,5,6,7,8,9,0,0,0,0 }); final RawFrame frame1 = inBuffer.read(readableChannel); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame1.getType())); Assertions.assertEquals(8, frame1.getFlags()); Assertions.assertEquals(8, frame1.getStreamId()); final ByteBuffer payload1 = frame1.getPayloadContent(); Assertions.assertNotNull(payload1); Assertions.assertEquals(5, payload1.remaining()); Assertions.assertEquals(0, payload1.get()); Assertions.assertEquals(1, payload1.get()); Assertions.assertEquals(2, payload1.get()); Assertions.assertEquals(3, payload1.get()); Assertions.assertEquals(4, payload1.get()); final RawFrame frame2 = inBuffer.read(readableChannel); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame2.getType())); Assertions.assertEquals(FrameFlag.of(FrameFlag.END_STREAM, FrameFlag.PADDED), frame2.getFlags()); Assertions.assertEquals(8, frame2.getStreamId()); final ByteBuffer payload2 = frame2.getPayloadContent(); Assertions.assertNotNull(payload2); Assertions.assertEquals(5, payload2.remaining()); Assertions.assertEquals(5, payload2.get()); Assertions.assertEquals(6, payload2.get()); Assertions.assertEquals(7, payload2.get()); Assertions.assertEquals(8, payload2.get()); Assertions.assertEquals(9, payload2.get()); Assertions.assertEquals(-1, readableChannel.read(ByteBuffer.allocate(1024))); } @Test public void testReadFrameMultipleSmallBuffer() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(new BasicH2TransportMetrics(), 20, 10); final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock( new byte[] { 0,0,10,0,8,0,0,0,8,4,1,1,1,1,1,0,0,0,0, 0,0,5,0,0,0,0,0,8,2,2,2,2,2, 0,0,10,0,9,0,0,0,8,4,3,3,3,3,3,0,0,0,0 }); final RawFrame frame1 = inBuffer.read(readableChannel); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame1.getType())); Assertions.assertEquals(8, frame1.getFlags()); Assertions.assertEquals(8, frame1.getStreamId()); final ByteBuffer payload1 = frame1.getPayloadContent(); Assertions.assertNotNull(payload1); Assertions.assertEquals(5, payload1.remaining()); Assertions.assertEquals(1, payload1.get()); Assertions.assertEquals(1, payload1.get()); Assertions.assertEquals(1, payload1.get()); Assertions.assertEquals(1, payload1.get()); Assertions.assertEquals(1, payload1.get()); final RawFrame frame2 = inBuffer.read(readableChannel); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame2.getType())); Assertions.assertEquals(0, frame2.getFlags()); Assertions.assertEquals(8, frame2.getStreamId()); final ByteBuffer payload2 = frame2.getPayloadContent(); Assertions.assertNotNull(payload2); Assertions.assertEquals(5, payload2.remaining()); Assertions.assertEquals(2, payload2.get()); Assertions.assertEquals(2, payload2.get()); Assertions.assertEquals(2, payload2.get()); Assertions.assertEquals(2, payload2.get()); Assertions.assertEquals(2, payload2.get()); final RawFrame frame3 = inBuffer.read(readableChannel); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame3.getType())); Assertions.assertEquals(FrameFlag.of(FrameFlag.END_STREAM, FrameFlag.PADDED), frame3.getFlags()); Assertions.assertEquals(8, frame3.getStreamId()); final ByteBuffer payload3 = frame3.getPayloadContent(); Assertions.assertNotNull(payload3); Assertions.assertEquals(5, payload3.remaining()); Assertions.assertEquals(3, payload3.get()); Assertions.assertEquals(3, payload3.get()); Assertions.assertEquals(3, payload3.get()); Assertions.assertEquals(3, payload3.get()); Assertions.assertEquals(3, payload3.get()); Assertions.assertEquals(-1, readableChannel.read(ByteBuffer.allocate(1024))); } @Test public void testReadFramePartialReads() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock( new byte[] {0,0}, new byte[] {10,0,9,0}, new byte[] {0,0,8}, new byte[] {4}, new byte[] {1,2,3,4}, new byte[] {5,0}, new byte[] {0,0,0}); final RawFrame frame = inBuffer.read(readableChannel); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame.getType())); Assertions.assertEquals(FrameFlag.of(FrameFlag.END_STREAM, FrameFlag.PADDED), frame.getFlags()); Assertions.assertEquals(8, frame.getStreamId()); final ByteBuffer payload = frame.getPayloadContent(); Assertions.assertNotNull(payload); Assertions.assertEquals(5, payload.remaining()); Assertions.assertEquals(1, payload.get()); Assertions.assertEquals(2, payload.get()); Assertions.assertEquals(3, payload.get()); Assertions.assertEquals(4, payload.get()); Assertions.assertEquals(5, payload.get()); Assertions.assertEquals(-1, readableChannel.read(ByteBuffer.allocate(1024))); } @Test public void testReadEmptyFrame() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock( new byte[] {0,0,0,0,0,0,0,0,0}); final RawFrame frame = inBuffer.read(readableChannel); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame.getType())); Assertions.assertEquals(0, frame.getFlags()); Assertions.assertEquals(0, frame.getStreamId()); final ByteBuffer payload = frame.getPayloadContent(); Assertions.assertNull(payload); } @Test public void testReadFrameConnectionClosed() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(new byte[] {}); Assertions.assertNull(inBuffer.read(readableChannel)); Assertions.assertThrows(ConnectionClosedException.class, () -> inBuffer.read(readableChannel)); } @Test public void testReadFrameCorruptFrame() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(new byte[] {0,0}); Assertions.assertThrows(H2CorruptFrameException.class, () -> inBuffer.read(readableChannel)); } @Test public void testWriteFrameExceedingLimit() throws Exception { final WritableByteChannelMock writableChannel = new WritableByteChannelMock(1024); final FrameOutputBuffer outbuffer = new FrameOutputBuffer(1024); final RawFrame frame = new RawFrame(FrameType.DATA.getValue(), 0, 1, ByteBuffer.wrap(new byte[2048])); Assertions.assertThrows(IllegalArgumentException.class, () -> outbuffer.write(frame, writableChannel)); } @Test public void testReadFrameExceedingLimit() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock( new byte[] {0,-128,-128,0,0,0,0,0,1}); Assertions.assertThrows(H2ConnectionException.class, () -> inBuffer.read(readableChannel)); } @Test public void testOutputBufferResize() throws Exception { final FrameOutputBuffer outBuffer = new FrameOutputBuffer(16 * 1024); Assertions.assertEquals(16 * 1024, outBuffer.getMaxFramePayloadSize()); outBuffer.resize(1024); Assertions.assertEquals(1024, outBuffer.getMaxFramePayloadSize()); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/TestDefaultH2RequestConverter.java0100664 0000000 0000000 00000050541 14403631147 030675 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl; import java.util.Arrays; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.net.URIAuthority; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDefaultH2RequestConverter { @Test public void testConvertFromFieldsBasic() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":path", "/"), new BasicHeader("custom123", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); final HttpRequest request = converter.convert(headers); Assertions.assertNotNull(request); Assertions.assertEquals("GET", request.getMethod()); Assertions.assertEquals("http", request.getScheme()); Assertions.assertEquals(new URIAuthority("www.example.com"), request.getAuthority()); Assertions.assertEquals("/", request.getPath()); final Header[] allHeaders = request.getHeaders(); Assertions.assertEquals(1, allHeaders.length); Assertions.assertEquals("custom123", allHeaders[0].getName()); Assertions.assertEquals("value", allHeaders[0].getValue()); } @Test public void testConvertFromFieldsUpperCaseHeaderName() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":Path", "/"), new BasicHeader("custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Header name ':Path' is invalid (header name contains uppercase characters)"); } @Test public void testConvertFromFieldsConnectionHeader() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":path", "/"), new BasicHeader("connection", "keep-alive")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Header 'connection: keep-alive' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsPseudoHeaderSequence() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader("custom", "value"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":path", "/")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Invalid sequence of headers (pseudo-headers must precede message headers)"); } @Test public void testConvertFromFieldsMissingMethod() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":scheme", "http"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":path", "/"), new BasicHeader("custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Mandatory request header ':method' not found"); } @Test public void testConvertFromFieldsMissingScheme() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":path", "/"), new BasicHeader("custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Mandatory request header ':scheme' not found"); } @Test public void testConvertFromFieldsMissingPath() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":authority", "www.example.com"), new BasicHeader("custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Mandatory request header ':path' not found"); } @Test public void testConvertFromFieldsUnknownPseudoHeader() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":path", "/"), new BasicHeader(":custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Unsupported request header ':custom'"); } @Test public void testConvertFromFieldsMultipleMethod() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":path", "/"), new BasicHeader("custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Multiple ':method' request headers are illegal"); } @Test public void testConvertFromFieldsMultipleScheme() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":scheme", "https"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":path", "/"), new BasicHeader("custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Multiple ':scheme' request headers are illegal"); } @Test public void testConvertFromFieldsMultiplePath() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "https"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":path", "/"), new BasicHeader(":path", "/"), new BasicHeader("custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Multiple ':path' request headers are illegal"); } @Test public void testConvertFromFieldsConnect() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "CONNECT"), new BasicHeader(":authority", "www.example.com"), new BasicHeader("custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); converter.convert(headers); } @Test public void testConvertFromFieldsConnectMissingAuthority() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "CONNECT"), new BasicHeader("custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Header ':authority' is mandatory for CONNECT request"); } @Test public void testConvertFromFieldsConnectPresentScheme() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "CONNECT"), new BasicHeader(":scheme", "http"), new BasicHeader(":authority", "www.example.com"), new BasicHeader("custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Header ':scheme' must not be set for CONNECT request"); } @Test public void testConvertFromFieldsConnectPresentPath() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "CONNECT"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":path", "/"), new BasicHeader("custom", "value")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(headers), "Header ':path' must not be set for CONNECT request"); } @Test public void testConvertFromMessageBasic() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); request.addHeader("custom123", "Value"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); final List
headers = converter.convert(request); Assertions.assertNotNull(headers); Assertions.assertEquals(5, headers.size()); final Header header1 = headers.get(0); Assertions.assertEquals(":method", header1.getName()); Assertions.assertEquals("GET", header1.getValue()); final Header header2 = headers.get(1); Assertions.assertEquals(":scheme", header2.getName()); Assertions.assertEquals("http", header2.getValue()); final Header header3 = headers.get(2); Assertions.assertEquals(":authority", header3.getName()); Assertions.assertEquals("host", header3.getValue()); final Header header4 = headers.get(3); Assertions.assertEquals(":path", header4.getName()); Assertions.assertEquals("/", header4.getValue()); final Header header5 = headers.get(4); Assertions.assertEquals("custom123", header5.getName()); Assertions.assertEquals("Value", header5.getValue()); } @Test public void testConvertFromMessageMissingScheme() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); request.addHeader("Custom123", "Value"); request.setScheme(null); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Request scheme is not set"); } @Test public void testConvertFromMessageMissingPath() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); request.addHeader("Custom123", "Value"); request.setPath(null); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Request path is not set"); } @Test public void testConvertFromMessageConnect() throws Exception { final HttpRequest request = new BasicHttpRequest("CONNECT", new HttpHost("host:80"), null); request.addHeader("custom123", "Value"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); final List
headers = converter.convert(request); Assertions.assertNotNull(headers); Assertions.assertEquals(3, headers.size()); final Header header1 = headers.get(0); Assertions.assertEquals(":method", header1.getName()); Assertions.assertEquals("CONNECT", header1.getValue()); final Header header2 = headers.get(1); Assertions.assertEquals(":authority", header2.getName()); Assertions.assertEquals("host:80", header2.getValue()); final Header header3 = headers.get(2); Assertions.assertEquals("custom123", header3.getName()); Assertions.assertEquals("Value", header3.getValue()); } @Test public void testConvertFromMessageConnectMissingAuthority() throws Exception { final HttpRequest request = new BasicHttpRequest("CONNECT", null, null); request.addHeader("Custom123", "Value"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "CONNECT request authority is not set"); } @Test public void testConvertFromMessageConnectWithPath() throws Exception { final HttpRequest request = new BasicHttpRequest("CONNECT", "/"); request.setAuthority(new URIAuthority("host")); request.addHeader("Custom123", "Value"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "CONNECT request path must be null"); } @Test public void testConvertFromMessageConnectionHeader() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); request.addHeader("Connection", "Keep-Alive"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Header 'Connection: Keep-Alive' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsKeepAliveHeader() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); request.addHeader("Keep-Alive", "timeout=5, max=1000"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Header 'Keep-Alive: timeout=5, max=1000' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsProxyConnectionHeader() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); request.addHeader("Proxy-Connection", "keep-alive"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Header 'Proxy-Connection: Keep-Alive' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsTransferEncodingHeader() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); request.addHeader("Transfer-Encoding", "gzip"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Header 'Transfer-Encoding: gzip' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsHostHeader() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); request.addHeader("Host", "host"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Header 'Host: host' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsUpgradeHeader() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); request.addHeader("Upgrade", "example/1, foo/2"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Header 'Upgrade: example/1, foo/2' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsTEHeader() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); request.addHeader("TE", "gzip"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Header 'TE: gzip' is illegal for HTTP/2 messages"); } @Test public void testConvertFromFieldsTETrailerHeader() throws Exception { final List
headers = Arrays.asList( new BasicHeader(":method", "GET"), new BasicHeader(":scheme", "http"), new BasicHeader(":authority", "www.example.com"), new BasicHeader(":path", "/"), new BasicHeader("te", "trailers")); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); final HttpRequest request = converter.convert(headers); Assertions.assertNotNull(request); Assertions.assertEquals("GET", request.getMethod()); Assertions.assertEquals("http", request.getScheme()); Assertions.assertEquals(new URIAuthority("www.example.com"), request.getAuthority()); Assertions.assertEquals("/", request.getPath()); final Header[] allHeaders = request.getHeaders(); Assertions.assertEquals(1, allHeaders.length); Assertions.assertEquals("te", allHeaders[0].getName()); Assertions.assertEquals("trailers", allHeaders[0].getValue()); } @Test public void testConvertFromMessageInvalidHeader() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); request.addHeader(":custom", "stuff"); final DefaultH2RequestConverter converter = new DefaultH2RequestConverter(); Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Header name ':custom' is invalid"); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/io/0040775 0000000 0000000 00000000000 14403631147 022560 5ustar000000000 0000000 httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/io/MultiByteArrayInputStream.java0100664 0000000 0000000 00000006536 14245617503 030547 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.io; import java.io.IOException; import java.io.InputStream; import java.util.ArrayDeque; import java.util.Queue; class MultiByteArrayInputStream extends InputStream { private final Queue bufs; private byte[] current; private int pos; public MultiByteArrayInputStream(final byte[]... bufs) { super(); this.bufs = new ArrayDeque<>(); for (final byte[] buf: bufs) { if (buf.length > 0) { this.bufs.add(buf); } } } private void advance() { if (this.current != null) { if (this.pos >= this.current.length) { this.current = null; } } if (this.current == null) { this.current = this.bufs.poll(); this.pos = 0; } } @Override public int read() throws IOException { advance(); if (this.current == null) { return -1; } return this.current[this.pos++]; } @Override public int read(final byte b[], final int off, final int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } advance(); if (this.current == null) { return -1; } final int chunk = Math.min(this.current.length - this.pos, len); if (chunk <= 0) { return 0; } System.arraycopy(this.current, this.pos, b, off, chunk); this.pos += chunk; return chunk; } @Override public long skip(final long n) { advance(); final int chunk = Math.min(this.current.length - this.pos, (int) n); if (chunk <= 0) { return 0; } this.pos += chunk; return chunk; } @Override public int available() { advance(); return this.current != null ? this.current.length - this.pos : 0; } @Override public boolean markSupported() { return false; } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/io/TestFrameInOutBuffers.java0100664 0000000 0000000 00000026607 14403631147 027621 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2.impl.io; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2CorruptFrameException; import org.apache.hc.core5.http2.frame.FrameConsts; import org.apache.hc.core5.http2.frame.FrameFlag; import org.apache.hc.core5.http2.frame.FrameType; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestFrameInOutBuffers { @Test public void testReadWriteFrame() throws Exception { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final FrameOutputBuffer outbuffer = new FrameOutputBuffer(16 * 1024); final RawFrame frame = new RawFrame(FrameType.DATA.getValue(), 0, 1, ByteBuffer.wrap(new byte[]{1,2,3,4,5})); outbuffer.write(frame, outputStream); final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final byte[] bytes = outputStream.toByteArray(); final ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); Assertions.assertEquals(FrameConsts.HEAD_LEN + 5, bytes.length); Assertions.assertEquals(1, outbuffer.getMetrics().getFramesTransferred()); Assertions.assertEquals(bytes.length, outbuffer.getMetrics().getBytesTransferred()); final RawFrame frame2 = inBuffer.read(inputStream); Assertions.assertEquals(FrameType.DATA.getValue(), frame2.getType()); Assertions.assertEquals(0, frame2.getFlags()); Assertions.assertEquals(1L, frame2.getStreamId()); final ByteBuffer payload2 = frame2.getPayloadContent(); Assertions.assertNotNull(payload2); Assertions.assertEquals(5, payload2.remaining()); Assertions.assertEquals(1, payload2.get()); Assertions.assertEquals(2, payload2.get()); Assertions.assertEquals(3, payload2.get()); Assertions.assertEquals(4, payload2.get()); Assertions.assertEquals(5, payload2.get()); Assertions.assertEquals(-1, inputStream.read()); Assertions.assertEquals(1, inBuffer.getMetrics().getFramesTransferred()); Assertions.assertEquals(bytes.length, inBuffer.getMetrics().getBytesTransferred()); } @Test public void testReadFrameMultiple() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final ByteArrayInputStream inputStream = new ByteArrayInputStream( new byte[] { 0,0,10,0,8,0,0,0,8,4,0,1,2,3,4,0,0,0,0, 0,0,10,0,9,0,0,0,8,4,5,6,7,8,9,0,0,0,0 }); final RawFrame frame1 = inBuffer.read(inputStream); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame1.getType())); Assertions.assertEquals(8, frame1.getFlags()); Assertions.assertEquals(8, frame1.getStreamId()); final ByteBuffer payload1 = frame1.getPayloadContent(); Assertions.assertNotNull(payload1); Assertions.assertEquals(5, payload1.remaining()); Assertions.assertEquals(0, payload1.get()); Assertions.assertEquals(1, payload1.get()); Assertions.assertEquals(2, payload1.get()); Assertions.assertEquals(3, payload1.get()); Assertions.assertEquals(4, payload1.get()); final RawFrame frame2 = inBuffer.read(inputStream); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame2.getType())); Assertions.assertEquals(FrameFlag.of(FrameFlag.END_STREAM, FrameFlag.PADDED), frame2.getFlags()); Assertions.assertEquals(8, frame2.getStreamId()); final ByteBuffer payload2 = frame2.getPayloadContent(); Assertions.assertNotNull(payload2); Assertions.assertEquals(5, payload2.remaining()); Assertions.assertEquals(5, payload2.get()); Assertions.assertEquals(6, payload2.get()); Assertions.assertEquals(7, payload2.get()); Assertions.assertEquals(8, payload2.get()); Assertions.assertEquals(9, payload2.get()); Assertions.assertEquals(-1, inputStream.read()); } @Test public void testReadFrameMultipleSmallBuffer() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(new BasicH2TransportMetrics(), 20, 10); final ByteArrayInputStream inputStream = new ByteArrayInputStream( new byte[] { 0,0,10,0,8,0,0,0,8,4,1,1,1,1,1,0,0,0,0, 0,0,5,0,0,0,0,0,8,2,2,2,2,2, 0,0,10,0,9,0,0,0,8,4,3,3,3,3,3,0,0,0,0 }); final RawFrame frame1 = inBuffer.read(inputStream); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame1.getType())); Assertions.assertEquals(8, frame1.getFlags()); Assertions.assertEquals(8, frame1.getStreamId()); final ByteBuffer payload1 = frame1.getPayloadContent(); Assertions.assertNotNull(payload1); Assertions.assertEquals(5, payload1.remaining()); Assertions.assertEquals(1, payload1.get()); Assertions.assertEquals(1, payload1.get()); Assertions.assertEquals(1, payload1.get()); Assertions.assertEquals(1, payload1.get()); Assertions.assertEquals(1, payload1.get()); final RawFrame frame2 = inBuffer.read(inputStream); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame2.getType())); Assertions.assertEquals(0, frame2.getFlags()); Assertions.assertEquals(8, frame2.getStreamId()); final ByteBuffer payload2 = frame2.getPayloadContent(); Assertions.assertNotNull(payload2); Assertions.assertEquals(5, payload2.remaining()); Assertions.assertEquals(2, payload2.get()); Assertions.assertEquals(2, payload2.get()); Assertions.assertEquals(2, payload2.get()); Assertions.assertEquals(2, payload2.get()); Assertions.assertEquals(2, payload2.get()); final RawFrame frame3 = inBuffer.read(inputStream); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame3.getType())); Assertions.assertEquals(FrameFlag.of(FrameFlag.END_STREAM, FrameFlag.PADDED), frame3.getFlags()); Assertions.assertEquals(8, frame3.getStreamId()); final ByteBuffer payload3 = frame3.getPayloadContent(); Assertions.assertNotNull(payload3); Assertions.assertEquals(5, payload3.remaining()); Assertions.assertEquals(3, payload3.get()); Assertions.assertEquals(3, payload3.get()); Assertions.assertEquals(3, payload3.get()); Assertions.assertEquals(3, payload3.get()); Assertions.assertEquals(3, payload3.get()); Assertions.assertEquals(-1, inputStream.read()); } @Test public void testReadFramePartialReads() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final MultiByteArrayInputStream inputStream = new MultiByteArrayInputStream( new byte[] {0,0}, new byte[] {10,0,9,0}, new byte[] {0,0,8}, new byte[] {4}, new byte[] {1,2,3,4}, new byte[] {5,0}, new byte[] {0,0,0}); final RawFrame frame = inBuffer.read(inputStream); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame.getType())); Assertions.assertEquals(FrameFlag.of(FrameFlag.END_STREAM, FrameFlag.PADDED), frame.getFlags()); Assertions.assertEquals(8, frame.getStreamId()); final ByteBuffer payload = frame.getPayloadContent(); Assertions.assertNotNull(payload); Assertions.assertEquals(5, payload.remaining()); Assertions.assertEquals(1, payload.get()); Assertions.assertEquals(2, payload.get()); Assertions.assertEquals(3, payload.get()); Assertions.assertEquals(4, payload.get()); Assertions.assertEquals(5, payload.get()); Assertions.assertEquals(-1, inputStream.read()); } @Test public void testReadEmptyFrame() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[] {0,0,0,0,0,0,0,0,0}); final RawFrame frame = inBuffer.read(inputStream); Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame.getType())); Assertions.assertEquals(0, frame.getFlags()); Assertions.assertEquals(0, frame.getStreamId()); final ByteBuffer payload = frame.getPayloadContent(); Assertions.assertNull(payload); } @Test public void testReadFrameConnectionClosed() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[] {}); Assertions.assertThrows(ConnectionClosedException.class, () -> inBuffer.read(inputStream)); } @Test public void testReadFrameCorruptFrame() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[] {0,0}); Assertions.assertThrows(H2CorruptFrameException.class, () -> inBuffer.read(inputStream)); } @Test public void testWriteFrameExceedingLimit() throws Exception { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final FrameOutputBuffer outbuffer = new FrameOutputBuffer(1024); final RawFrame frame = new RawFrame(FrameType.DATA.getValue(), 0, 1, ByteBuffer.wrap(new byte[2048])); Assertions.assertThrows(H2ConnectionException.class, () -> outbuffer.write(frame, outputStream)); } @Test public void testReadFrameExceedingLimit() throws Exception { final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024); final ByteArrayInputStream inputStream = new ByteArrayInputStream( new byte[] {0,-128,-128,0,0,0,0,0,1}); Assertions.assertThrows(H2ConnectionException.class, () -> inBuffer.read(inputStream)); } } httpcore5-h2/src/test/java/org/apache/hc/core5/http2/WritableByteChannelMock.java0100664 0000000 0000000 00000006730 14245617503 026562 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.http2; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.WritableByteChannel; public class WritableByteChannelMock implements WritableByteChannel { private final int capacityLimit; private int capacityUsed; private ByteBuffer buf; private boolean closed; public WritableByteChannelMock(final int initialSize, final int capacityLimit) { this.buf = ByteBuffer.allocate(initialSize); this.capacityLimit = capacityLimit; } public WritableByteChannelMock(final int initialSize) { this(initialSize, 0); } private void expandCapacity(final int capacity) { final ByteBuffer oldbuffer = this.buf; this.buf = ByteBuffer.allocate(capacity); oldbuffer.flip(); this.buf.put(oldbuffer); } private void ensureCapacity(final int requiredCapacity) { if (requiredCapacity > this.buf.capacity()) { expandCapacity(requiredCapacity); } } @Override public int write(final ByteBuffer src) throws IOException { if (this.closed) { throw new ClosedChannelException(); } final int len = src.remaining(); ensureCapacity(this.buf.position() + len); if (this.capacityLimit > 0) { final int chunk = Math.min(this.capacityLimit - this.capacityUsed, len); if (chunk > 0) { final int limit = src.limit(); src.limit(src.position() + chunk); this.buf.put(src); src.limit(limit); this.capacityUsed += chunk; return chunk; } return 0; } this.buf.put(src); return len; } @Override public boolean isOpen() { return !this.closed; } @Override public void close() throws IOException { this.closed = true; } public void flush() { this.capacityUsed = 0; } public void reset() { this.capacityUsed = 0; this.buf.clear(); } public byte[] toByteArray() { final ByteBuffer dup = this.buf.duplicate(); dup.flip(); final byte[] bytes = new byte[dup.remaining()]; dup.get(bytes); return bytes; } }httpcore5-h2/pom.xml0100664 0000000 0000000 00000007040 14443065161 013355 0ustar000000000 0000000 4.0.0 org.apache.httpcomponents.core5 httpcore5-parent 5.2.2 httpcore5-h2 Apache HttpComponents Core HTTP/2 Apache HttpComponents HTTP/2 Core Components org.apache.httpcomponents.core5.httpcore5.h2 org.apache.httpcomponents.core5 httpcore5 org.conscrypt conscrypt-openjdk-uber true org.junit.jupiter junit-jupiter test org.hamcrest hamcrest test org.mockito mockito-core test org.slf4j slf4j-api test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test maven-project-info-reports-plugin false index dependencies dependency-info summary SECURITY.md0100664 0000000 0000000 00000001736 14403631147 011412 0ustar000000000 0000000 # Reporting a vulnerability If you believe you found a vulnerability in Apache HttpClient, please contact [the Apache Security Team](https://www.apache.org/security/). README.md0100664 0000000 0000000 00000006656 14403631147 011106 0ustar000000000 0000000 Apache HttpComponents Core ========================== Welcome to the HttpCore component of the Apache HttpComponents project. [![GitHub Actions Status](https://github.com/apache/httpcomponents-core/workflows/Java%20CI/badge.svg)](https://github.com/apache/httpcomponents-core/actions) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.httpcomponents.core5/httpcore5/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.apache.httpcomponents.core5/httpcore5) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) Building Instructions --------------------- For building from source instructions please refer to [BUILDING.txt](./BUILDING.txt). Dependencies ------------ HttpCore requires Java 1.8 compatible runtime. Licensing --------- Apache HttpComponents Core is licensed under the Apache License 2.0. See the files [LICENSE.txt](./LICENSE.txt) and [NOTICE.txt](./NOTICE.txt) for more information. Contact ------- - For general information visit the main project site at https://hc.apache.org/ - For current status information visit the status page at https://hc.apache.org/status.html - If you want to contribute visit https://hc.apache.org/get-involved.html Cryptographic Software Notice ----------------------------- This distribution may include software that has been designed for use with cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. See https://www.wassenaar.org/ for more information. The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing cryptographic functions with asymmetric algorithms. The form and manner of this Apache Software Foundation distribution makes it eligible for export under the License Exception ENC Technology Software Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Section 740.13) for both object code and source code. The following provides more details on the included software that may be subject to export controls on cryptographic software: > Apache HttpComponents Core interfaces with the > Java Secure Socket Extension (JSSE) API to provide > - HTTPS support > > Apache HttpComponents Core does not include any > implementation of JSSE. pom.xml0100664 0000000 0000000 00000032632 14443065161 011136 0ustar000000000 0000000 org.apache.httpcomponents httpcomponents-parent 13 4.0.0 org.apache.httpcomponents.core5 httpcore5-parent Apache HttpComponents Core Parent 5.2.2 Apache HttpComponents Core is a library of components for building HTTP enabled services https://hc.apache.org/httpcomponents-core-5.2.x/${project.version}/ 2005 pom Jira https://issues.apache.org/jira/browse/HTTPCORE scm:git:https://gitbox.apache.org/repos/asf/httpcomponents-core.git scm:git:https://gitbox.apache.org/repos/asf/httpcomponents-core.git https://github.com/apache/httpcomponents-core/tree/${project.scm.tag} 5.2.2 apache.website Apache HttpComponents Website scm:svn:https://svn.apache.org/repos/asf/httpcomponents/site/components/httpcomponents-core-5.2.x/LATEST/ httpcore5 httpcore5-h2 httpcore5-reactive httpcore5-testing 1.8 1.8 true 2.5.2 5.9.3 2.2 5.0.0 4.11.0 1.7.36 2.19.0 2.2.21 3.1.6 5.2 javax.net.ssl.SSLEngine,javax.net.ssl.SSLParameters,java.nio.ByteBuffer,java.nio.CharBuffer org.apache.httpcomponents.core5 httpcore5 ${project.version} org.apache.httpcomponents.core5 httpcore5-h2 ${project.version} org.apache.httpcomponents.core5 httpcore5-reactive ${project.version} org.apache.httpcomponents.core5 httpcore5-testing ${project.version} org.conscrypt conscrypt-openjdk-uber ${conscrypt.version} org.junit junit-bom ${junit.version} pom import org.hamcrest hamcrest ${hamcrest.version} test org.mockito mockito-core ${mockito.version} test org.slf4j slf4j-api ${slf4j.version} org.apache.logging.log4j log4j-slf4j-impl ${log4j.version} org.apache.logging.log4j log4j-core ${log4j.version} clean verify maven-jar-plugin ${Automatic-Module-Name} ${project.url} maven-javadoc-plugin ${project.url}/httpcore5/apidocs/ ${project.url}/httpcore5-h2/apidocs/ org.apache.maven.plugins maven-checkstyle-plugin validate-main validate hc-stylecheck/default.xml hc-stylecheck/asl2.header true true false ${basedir}/src/main ${basedir}/src/test checkstyle com.github.siom79.japicmp japicmp-maven-plugin ${project.groupId} ${project.artifactId} ${api.comparison.version} jar ${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging} true true METHOD_NEW_DEFAULT true true @org.apache.hc.core5.annotation.Internal verify cmp org.apache.rat apache-rat-plugin verify check **/.checkstyle **/.pmd **/*.iml **/.externalToolBuilders/** maven-eclipse.xml src/docbkx/resources/** src/test/resources/*.truststore src/test/resources/*.p12 **/.dockerignore bin/** maven-project-info-reports-plugin false index dependency-info dependency-management issue-management licenses mailing-lists scm summary maven-javadoc-plugin true ${project.url}/httpcore5/apidocs/ ${project.url}/httpcore5-h2/apidocs/ true Apache HttpCore HTTP/1.1 org.apache.hc.core5* Apache HttpCore HTTP/2 org.apache.hc.core5.http2* Apache HttpCore Reactive Streams Bindings org.apache.hc.core5.reactive* Apache HttpCore Testing org.apache.hc.core5.testing*:org.apache.hc.core5.benchmark* javadoc aggregate com.github.siom79.japicmp japicmp-maven-plugin cmp-report ${project.groupId} ${project.artifactId} ${api.comparison.version} jar ${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging} @org.apache.hc.core5.annotation.Internal true maven-jxr-plugin maven-surefire-report-plugin httpcore5-reactive/0040775 0000000 0000000 00000000000 14443065100 013324 5ustar000000000 0000000 httpcore5-reactive/src/0040775 0000000 0000000 00000000000 14245617503 014125 5ustar000000000 0000000 httpcore5-reactive/src/main/0040775 0000000 0000000 00000000000 14245617503 015051 5ustar000000000 0000000 httpcore5-reactive/src/main/java/0040775 0000000 0000000 00000000000 14245617503 015772 5ustar000000000 0000000 httpcore5-reactive/src/main/java/org/0040775 0000000 0000000 00000000000 14245617503 016561 5ustar000000000 0000000 httpcore5-reactive/src/main/java/org/apache/0040775 0000000 0000000 00000000000 14245617503 020002 5ustar000000000 0000000 httpcore5-reactive/src/main/java/org/apache/hc/0040775 0000000 0000000 00000000000 14245617503 020374 5ustar000000000 0000000 httpcore5-reactive/src/main/java/org/apache/hc/core5/0040775 0000000 0000000 00000000000 14245617503 021411 5ustar000000000 0000000 httpcore5-reactive/src/main/java/org/apache/hc/core5/reactive/0040775 0000000 0000000 00000000000 14435411677 023220 5ustar000000000 0000000 httpcore5-reactive/src/main/java/org/apache/hc/core5/reactive/ReactiveDataProducer.java0100664 0000000 0000000 00000013477 14315123013 030111 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpStreamResetException; import org.apache.hc.core5.http.nio.AsyncDataProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.util.Args; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; /** * An asynchronous data producer that supports Reactive Streams. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) final class ReactiveDataProducer implements AsyncDataProducer, Subscriber { private static final int BUFFER_WINDOW_SIZE = 5; private final AtomicReference requestChannel = new AtomicReference<>(); private final AtomicReference exception = new AtomicReference<>(); private final AtomicBoolean complete = new AtomicBoolean(false); private final Publisher publisher; private final AtomicReference subscription = new AtomicReference<>(); private final ArrayDeque buffers = new ArrayDeque<>(); // This field requires synchronization public ReactiveDataProducer(final Publisher publisher) { this.publisher = Args.notNull(publisher, "publisher"); } void setChannel(final DataStreamChannel channel) { requestChannel.set(channel); } @Override public void onSubscribe(final Subscription subscription) { if (this.subscription.getAndSet(subscription) != null) { throw new IllegalStateException("Already subscribed"); } subscription.request(BUFFER_WINDOW_SIZE); } @Override public void onNext(final ByteBuffer byteBuffer) { final byte[] copy = new byte[byteBuffer.remaining()]; byteBuffer.get(copy); synchronized (buffers) { buffers.add(ByteBuffer.wrap(copy)); } signalReadiness(); } @Override public void onError(final Throwable throwable) { subscription.set(null); exception.set(throwable); signalReadiness(); } @Override public void onComplete() { subscription.set(null); complete.set(true); signalReadiness(); } private void signalReadiness() { final DataStreamChannel channel = requestChannel.get(); if (channel == null) { throw new IllegalStateException("Output channel is not set"); } channel.requestOutput(); } @Override public int available() { if (exception.get() != null || complete.get()) { return 1; } else { synchronized (buffers) { int sum = 0; for (final ByteBuffer buffer : buffers) { sum += buffer.remaining(); } return sum; } } } @Override public void produce(final DataStreamChannel channel) throws IOException { if (requestChannel.get() == null) { requestChannel.set(channel); publisher.subscribe(this); } final Throwable t = exception.get(); final Subscription s = subscription.get(); int buffersToReplenish = 0; try { synchronized (buffers) { if (t != null) { throw new HttpStreamResetException(t.getMessage(), t); } else if (this.complete.get() && buffers.isEmpty()) { channel.endStream(); } else { while (!buffers.isEmpty()) { final ByteBuffer nextBuffer = buffers.remove(); channel.write(nextBuffer); if (nextBuffer.remaining() > 0) { buffers.push(nextBuffer); break; } else if (s != null) { // We defer the #request call until after we release the buffer lock. buffersToReplenish++; } } } } } finally { if (s != null && buffersToReplenish > 0) { s.request(buffersToReplenish); } } } @Override public void releaseResources() { final Subscription s = subscription.getAndSet(null); if (s != null) { s.cancel(); } } } httpcore5-reactive/src/main/java/org/apache/hc/core5/reactive/ReactiveRequestProcessor.java0100664 0000000 0000000 00000005133 14245617503 031070 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.reactivestreams.Publisher; import java.io.IOException; import java.nio.ByteBuffer; /** * @since 5.0 */ public interface ReactiveRequestProcessor { /** * Processes the actual HTTP request. The handler can choose to send * response messages immediately inside the call or asynchronously * at some later point. * * @param request the actual request. * @param entityDetails the request entity details or {@code null} if the request * does not enclose an entity. * @param responseChannel the response channel. * @param context the actual execution context. * @param requestBody a reactive stream representing the request body. * @param responseBodyCallback a callback to invoke with the response body, if any. */ void processRequest( HttpRequest request, EntityDetails entityDetails, ResponseChannel responseChannel, HttpContext context, Publisher requestBody, Callback> responseBodyCallback) throws HttpException, IOException; } httpcore5-reactive/src/main/java/org/apache/hc/core5/reactive/ReactiveResponseConsumer.java0100664 0000000 0000000 00000014074 14403631147 031052 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Future; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.reactivestreams.Publisher; /** * An {@link AsyncResponseConsumer} that publishes the response body through * a {@link Publisher}, as defined by the Reactive Streams specification. The * response is represented as a {@link Message} consisting of a {@link * HttpResponse} representing the headers and a {@link Publisher} representing * the response body as an asynchronous stream of {@link ByteBuffer} instances. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public final class ReactiveResponseConsumer implements AsyncResponseConsumer { private final ReactiveDataConsumer reactiveDataConsumer = new ReactiveDataConsumer(); private final List
trailers = Collections.synchronizedList(new ArrayList<>()); private final BasicFuture>> responseFuture; private volatile BasicFuture responseCompletion; private volatile HttpResponse informationResponse; private volatile EntityDetails entityDetails; /** * Creates a {@code ReactiveResponseConsumer}. */ public ReactiveResponseConsumer() { this.responseFuture = new BasicFuture<>(null); } /** * Creates a {@code ReactiveResponseConsumer} that will call back the supplied {@link FutureCallback} with a * streamable response. * * @param responseCallback the callback to invoke when the response is available for consumption. */ public ReactiveResponseConsumer(final FutureCallback>> responseCallback) { this.responseFuture = new BasicFuture<>(Args.notNull(responseCallback, "responseCallback")); } public Future>> getResponseFuture() { return responseFuture; } /** * Returns the intermediate (1xx) HTTP response if one was received. * * @return the information response, or {@code null} if none. */ public HttpResponse getInformationResponse() { return informationResponse; } /** * Returns the response entity details. * * @return the entity details, or {@code null} if none. */ public EntityDetails getEntityDetails() { return entityDetails; } /** * Returns the trailers received at the end of the response. * * @return a non-null list of zero or more trailers. */ public List
getTrailers() { return trailers; } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext httpContext, final FutureCallback resultCallback ) { this.entityDetails = entityDetails; this.responseCompletion = new BasicFuture<>(resultCallback); this.responseFuture.completed(new Message<>(response, reactiveDataConsumer)); if (entityDetails == null) { streamEnd(null); } } @Override public void informationResponse(final HttpResponse response, final HttpContext httpContext) { this.informationResponse = response; } @Override public void failed(final Exception cause) { reactiveDataConsumer.failed(cause); responseFuture.failed(cause); if (responseCompletion != null) { responseCompletion.failed(cause); } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { reactiveDataConsumer.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { reactiveDataConsumer.consume(src); } @Override public void streamEnd(final List trailers) { if (trailers != null) { this.trailers.addAll(trailers); } reactiveDataConsumer.streamEnd(trailers); responseCompletion.completed(null); } @Override public void releaseResources() { reactiveDataConsumer.releaseResources(); responseFuture.cancel(); if (responseCompletion != null) { responseCompletion.cancel(); } } } httpcore5-reactive/src/main/java/org/apache/hc/core5/reactive/ReactiveServerExchangeHandler.java0100664 0000000 0000000 00000011551 14403631147 031744 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.reactivestreams.Publisher; /** * An implementation of {@link AsyncServerExchangeHandler} designed to work with reactive streams. * * @since 5.0 */ public final class ReactiveServerExchangeHandler implements AsyncServerExchangeHandler { private final ReactiveRequestProcessor requestProcessor; private final AtomicReference responseProducer = new AtomicReference<>(); private final ReactiveDataConsumer requestConsumer; private volatile DataStreamChannel channel; /** * Creates a {@code ReactiveServerExchangeHandler}. * * @param requestProcessor the {@link ReactiveRequestProcessor} instance to * invoke when the request is ready to be handled. */ public ReactiveServerExchangeHandler(final ReactiveRequestProcessor requestProcessor) { this.requestProcessor = requestProcessor; this.requestConsumer = new ReactiveDataConsumer(); } @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context ) throws HttpException, IOException { final Callback> callback = result -> { final ReactiveDataProducer producer = new ReactiveDataProducer(result); if (channel != null) { producer.setChannel(channel); } responseProducer.set(producer); result.subscribe(producer); }; requestProcessor.processRequest(request, entityDetails, responseChannel, context, requestConsumer, callback); } @Override public void failed(final Exception cause) { requestConsumer.failed(cause); final ReactiveDataProducer p = responseProducer.get(); if (p != null) { p.onError(cause); } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { requestConsumer.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { requestConsumer.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { requestConsumer.streamEnd(trailers); } @Override public int available() { final ReactiveDataProducer p = responseProducer.get(); if (p == null) { return 0; } else { return p.available(); } } @Override public void produce(final DataStreamChannel channel) throws IOException { this.channel = channel; final ReactiveDataProducer p = responseProducer.get(); if (p != null) { p.produce(channel); } } @Override public void releaseResources() { final ReactiveDataProducer p = responseProducer.get(); if (p != null) { p.releaseResources(); } requestConsumer.releaseResources(); } } httpcore5-reactive/src/main/java/org/apache/hc/core5/reactive/ReactiveDataConsumer.java0100664 0000000 0000000 00000014656 14245617503 030137 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStreamResetException; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.util.Args; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; /** * An asynchronous data consumer that supports Reactive Streams. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) final class ReactiveDataConsumer implements AsyncDataConsumer, Publisher { private final AtomicLong requests = new AtomicLong(0); private final BlockingQueue buffers = new LinkedBlockingQueue<>(); private final AtomicBoolean flushInProgress = new AtomicBoolean(false); private final Object flushLock = new Object(); private final AtomicInteger windowScalingIncrement = new AtomicInteger(0); private volatile boolean cancelled; private volatile boolean completed; private volatile Exception exception; private volatile CapacityChannel capacityChannel; private volatile Subscriber subscriber; public void failed(final Exception cause) { if (!completed) { exception = cause; flushToSubscriber(); } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { throwIfCancelled(); this.capacityChannel = capacityChannel; signalCapacity(capacityChannel); } private void signalCapacity(final CapacityChannel channel) throws IOException { final int increment = windowScalingIncrement.getAndSet(0); if (increment > 0) { channel.update(increment); } } private void throwIfCancelled() throws IOException { if (cancelled) { throw new HttpStreamResetException("Downstream subscriber to ReactiveDataConsumer cancelled"); } } @Override public void consume(final ByteBuffer byteBuffer) throws IOException { if (completed) { throw new IllegalStateException("Received data past end of stream"); } throwIfCancelled(); final byte[] copy = new byte[byteBuffer.remaining()]; byteBuffer.get(copy); buffers.add(ByteBuffer.wrap(copy)); flushToSubscriber(); } @Override public void streamEnd(final List trailers) { completed = true; flushToSubscriber(); } @Override public void releaseResources() { this.capacityChannel = null; } private void flushToSubscriber() { synchronized (flushLock) { final Subscriber s = subscriber; if (flushInProgress.getAndSet(true)) { return; } try { if (s == null) { return; } if (exception != null) { subscriber = null; s.onError(exception); return; } ByteBuffer next; while (requests.get() > 0 && ((next = buffers.poll()) != null)) { final int bytesFreed = next.remaining(); s.onNext(next); requests.decrementAndGet(); windowScalingIncrement.addAndGet(bytesFreed); } final CapacityChannel localChannel = capacityChannel; if (localChannel != null) { try { signalCapacity(localChannel); } catch (final IOException e) { exception = e; s.onError(e); return; } } if (completed && buffers.isEmpty()) { subscriber = null; s.onComplete(); } } finally { flushInProgress.set(false); } } } @Override public void subscribe(final Subscriber subscriber) { this.subscriber = Args.notNull(subscriber, "subscriber"); subscriber.onSubscribe(new Subscription() { @Override public void request(final long increment) { if (increment <= 0) { failed(new IllegalArgumentException("The number of elements requested must be strictly positive")); return; } requests.addAndGet(increment); flushToSubscriber(); } @Override public void cancel() { ReactiveDataConsumer.this.cancelled = true; ReactiveDataConsumer.this.subscriber = null; } }); } } httpcore5-reactive/src/main/java/org/apache/hc/core5/reactive/ReactiveEntityProducer.java0100664 0000000 0000000 00000007533 14435411677 030533 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.reactivestreams.Publisher; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Set; /** * An {@link AsyncEntityProducer} that subscribes to a {@code Publisher} * instance, as defined by the Reactive Streams specification. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public final class ReactiveEntityProducer implements AsyncEntityProducer { private final ReactiveDataProducer reactiveDataProducer; private final long contentLength; private final ContentType contentType; private final String contentEncoding; /** * Creates a new {@code ReactiveEntityProducer} with the given parameters. * * @param publisher the publisher of the entity stream. * @param contentLength the length of the entity, or -1 if unknown (implies chunked encoding). * @param contentType the {@code Content-Type} of the entity, or null if none. * @param contentEncoding the {@code Content-Encoding} of the entity, or null if none. */ public ReactiveEntityProducer( final Publisher publisher, final long contentLength, final ContentType contentType, final String contentEncoding ) { this.reactiveDataProducer = new ReactiveDataProducer(publisher); this.contentLength = contentLength; this.contentType = contentType; this.contentEncoding = contentEncoding; } @Override public int available() { return reactiveDataProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { reactiveDataProducer.produce(channel); } @Override public void releaseResources() { reactiveDataProducer.releaseResources(); } @Override public boolean isRepeatable() { return false; } @Override public void failed(final Exception cause) { releaseResources(); } @Override public long getContentLength() { return contentLength; } @Override public String getContentType() { return contentType != null ? contentType.toString() : null; } @Override public String getContentEncoding() { return contentEncoding; } @Override public boolean isChunked() { return contentLength == -1; } @Override public Set getTrailerNames() { return null; } } httpcore5-reactive/src/test/0040775 0000000 0000000 00000000000 14245617503 015104 5ustar000000000 0000000 httpcore5-reactive/src/test/java/0040775 0000000 0000000 00000000000 14245617503 016025 5ustar000000000 0000000 httpcore5-reactive/src/test/java/org/0040775 0000000 0000000 00000000000 14245617503 016614 5ustar000000000 0000000 httpcore5-reactive/src/test/java/org/apache/0040775 0000000 0000000 00000000000 14245617503 020035 5ustar000000000 0000000 httpcore5-reactive/src/test/java/org/apache/hc/0040775 0000000 0000000 00000000000 14245617503 020427 5ustar000000000 0000000 httpcore5-reactive/src/test/java/org/apache/hc/core5/0040775 0000000 0000000 00000000000 14245617503 021444 5ustar000000000 0000000 httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/0040775 0000000 0000000 00000000000 14435411677 023253 5ustar000000000 0000000 httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/TestReactiveDataConsumer.java0100664 0000000 0000000 00000020224 14403631147 031012 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Notification; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; import org.apache.hc.core5.http.HttpStreamResetException; import org.apache.hc.core5.http.nio.CapacityChannel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; public class TestReactiveDataConsumer { @Test public void testStreamThatEndsNormally() throws Exception { final ReactiveDataConsumer consumer = new ReactiveDataConsumer(); final List output = Collections.synchronizedList(new ArrayList<>()); final CountDownLatch complete = new CountDownLatch(1); Observable.fromPublisher(consumer) .materialize() .forEach(byteBufferNotification -> { if (byteBufferNotification.isOnComplete()) { complete.countDown(); } else if (byteBufferNotification.isOnNext()) { output.add(byteBufferNotification.getValue()); } else { throw new IllegalArgumentException(); } }); consumer.consume(ByteBuffer.wrap(new byte[]{ '1' })); consumer.consume(ByteBuffer.wrap(new byte[]{ '2' })); consumer.consume(ByteBuffer.wrap(new byte[]{ '3' })); consumer.streamEnd(null); Assertions.assertTrue(complete.await(1, TimeUnit.SECONDS), "Stream did not finish before timeout"); Assertions.assertEquals(3, output.size()); Assertions.assertEquals(ByteBuffer.wrap(new byte[]{ '1' }), output.get(0)); Assertions.assertEquals(ByteBuffer.wrap(new byte[]{ '2' }), output.get(1)); Assertions.assertEquals(ByteBuffer.wrap(new byte[]{ '3' }), output.get(2)); } @Test public void testStreamThatEndsWithError() { final ReactiveDataConsumer consumer = new ReactiveDataConsumer(); final Single>> single = Observable.fromPublisher(consumer) .materialize() .toList(); final Exception ex = new RuntimeException(); consumer.failed(ex); Assertions.assertSame(ex, single.blockingGet().get(0).getError()); } @Test public void testCancellation() throws Exception { final ReactiveDataConsumer consumer = new ReactiveDataConsumer(); consumer.subscribe(new Subscriber() { @Override public void onSubscribe(final Subscription s) { s.cancel(); } @Override public void onNext(final ByteBuffer byteBuffer) { } @Override public void onError(final Throwable throwable) { } @Override public void onComplete() { } }); Assertions.assertThrows(HttpStreamResetException.class, () -> consumer.consume(ByteBuffer.wrap(new byte[1024]))); } @Test public void testCapacityIncrements() throws Exception { final ReactiveDataConsumer consumer = new ReactiveDataConsumer(); final ByteBuffer data = ByteBuffer.wrap(new byte[1024]); final AtomicInteger lastIncrement = new AtomicInteger(-1); final CapacityChannel channel = lastIncrement::set; consumer.updateCapacity(channel); Assertions.assertEquals(-1, lastIncrement.get(), "CapacityChannel#update should not have been invoked yet"); final AtomicInteger received = new AtomicInteger(0); final AtomicReference subscription = new AtomicReference<>(); consumer.subscribe(new Subscriber() { @Override public void onSubscribe(final Subscription s) { subscription.set(s); } @Override public void onNext(final ByteBuffer byteBuffer) { received.incrementAndGet(); } @Override public void onError(final Throwable throwable) { } @Override public void onComplete() { } }); consumer.consume(data.duplicate()); consumer.consume(data.duplicate()); consumer.consume(data.duplicate()); consumer.consume(data.duplicate()); subscription.get().request(1); Assertions.assertEquals(1024, lastIncrement.get()); subscription.get().request(2); Assertions.assertEquals(2 * 1024, lastIncrement.get()); subscription.get().request(99); Assertions.assertEquals(1024, lastIncrement.get()); } @Test public void testFullResponseBuffering() throws Exception { // Due to inherent race conditions, is possible for the entire response to be buffered and completed before // the Subscriber shows up. This must be handled correctly. final ReactiveDataConsumer consumer = new ReactiveDataConsumer(); final ByteBuffer data = ByteBuffer.wrap(new byte[1024]); consumer.consume(data.duplicate()); consumer.consume(data.duplicate()); consumer.consume(data.duplicate()); consumer.streamEnd(null); Assertions.assertEquals(Flowable.fromPublisher(consumer).count().blockingGet().longValue(), 3L); } @Test public void testErrorBuffering() throws Exception { final ReactiveDataConsumer consumer = new ReactiveDataConsumer(); final ByteBuffer data = ByteBuffer.wrap(new byte[1024]); final RuntimeException ex = new RuntimeException(); consumer.consume(data.duplicate()); consumer.consume(data.duplicate()); consumer.consume(data.duplicate()); consumer.failed(ex); final Notification result = Flowable.fromPublisher(consumer) .materialize() .singleOrError() .blockingGet(); Assertions.assertSame(ex, result.getError()); } @Test public void testFailAfterCompletion() { // Calling consumer.failed() after consumer.streamEnd() must be a no-op. // The exception must be discarded, and the subscriber must see that // the stream was successfully completed. final ReactiveDataConsumer consumer = new ReactiveDataConsumer(); consumer.streamEnd(null); final RuntimeException ex = new RuntimeException(); consumer.failed(ex); final Notification result = Flowable.fromPublisher(consumer) .materialize() .singleOrError() .blockingGet(); Assertions.assertFalse(result.isOnError()); Assertions.assertTrue(result.isOnComplete()); } } httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/BasicDataStreamChannel.java0100664 0000000 0000000 00000004556 14245617503 030400 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.nio.DataStreamChannel; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.util.ArrayList; import java.util.List; public class BasicDataStreamChannel implements DataStreamChannel { private final WritableByteChannel byteChannel; private List
trailers; public BasicDataStreamChannel(final WritableByteChannel byteChannel) { this.byteChannel = byteChannel; } @Override public void requestOutput() { } @Override public int write(final ByteBuffer src) throws IOException { return byteChannel.write(src); } @Override public void endStream() throws IOException { if (byteChannel.isOpen()) { byteChannel.close(); } } @Override public void endStream(final List trailers) throws IOException { endStream(); if (trailers != null) { this.trailers = new ArrayList<>(); this.trailers.addAll(trailers); } } public List
getTrailers() { return trailers; } } httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/TestReactiveDataProducer.java0100664 0000000 0000000 00000007270 14403631147 031010 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.HttpStreamResetException; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.reactivex.rxjava3.core.Flowable; public class TestReactiveDataProducer { @Test public void testStreamThatEndsNormally() throws Exception { final Flowable publisher = Flowable.just( ByteBuffer.wrap(new byte[]{ '1', '2', '3' }), ByteBuffer.wrap(new byte[]{ '4', '5', '6' })); final ReactiveDataProducer producer = new ReactiveDataProducer(publisher); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("123456", byteChannel.dump(StandardCharsets.US_ASCII)); producer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen()); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); } @Test public void testStreamThatEndsWithError() throws Exception { final Flowable publisher = Flowable.concatArray( Flowable.just( ByteBuffer.wrap(new byte[]{ '1' }), ByteBuffer.wrap(new byte[]{ '2' }), ByteBuffer.wrap(new byte[]{ '3' }), ByteBuffer.wrap(new byte[]{ '4' }), ByteBuffer.wrap(new byte[]{ '5' }), ByteBuffer.wrap(new byte[]{ '6' })), Flowable.error(new RuntimeException()) ); final ReactiveDataProducer producer = new ReactiveDataProducer(publisher); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); producer.produce(streamChannel); Assertions.assertEquals("12345", byteChannel.dump(StandardCharsets.US_ASCII)); final HttpStreamResetException exception = Assertions.assertThrows(HttpStreamResetException.class, () -> producer.produce(streamChannel)); Assertions.assertTrue(exception.getCause() instanceof RuntimeException, "Expected published exception to be rethrown"); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); } } httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/TestReactiveEntityProducer.java0100664 0000000 0000000 00000012765 14435411677 031431 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpStreamResetException; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.reactivex.rxjava3.core.Flowable; public class TestReactiveEntityProducer { private static final long CONTENT_LENGTH = 1; private static final ContentType CONTENT_TYPE = ContentType.APPLICATION_JSON; private static final String GZIP_CONTENT_ENCODING = "gzip"; @Test public void testStreamThatEndsNormally() throws Exception { final Flowable publisher = Flowable.just( ByteBuffer.wrap(new byte[]{'1', '2', '3'}), ByteBuffer.wrap(new byte[]{'4', '5', '6'})); final ReactiveEntityProducer entityProducer = new ReactiveEntityProducer(publisher, CONTENT_LENGTH, CONTENT_TYPE, GZIP_CONTENT_ENCODING); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); entityProducer.produce(streamChannel); Assertions.assertTrue(byteChannel.isOpen(), "Should be open"); Assertions.assertEquals("123456", byteChannel.dump(StandardCharsets.US_ASCII)); entityProducer.produce(streamChannel); Assertions.assertFalse(byteChannel.isOpen(), "Should be closed"); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); Assertions.assertFalse(entityProducer.isChunked()); Assertions.assertEquals(GZIP_CONTENT_ENCODING, entityProducer.getContentEncoding()); Assertions.assertNull(entityProducer.getTrailerNames()); Assertions.assertEquals(CONTENT_LENGTH, entityProducer.getContentLength()); Assertions.assertEquals(CONTENT_TYPE.toString(), entityProducer.getContentType()); Assertions.assertFalse(entityProducer.isRepeatable()); Assertions.assertEquals(1, entityProducer.available()); entityProducer.releaseResources(); } @Test public void testStreamThatEndsWithError() throws Exception { final Flowable publisher = Flowable.concatArray( Flowable.just( ByteBuffer.wrap(new byte[]{'1'}), ByteBuffer.wrap(new byte[]{'2'}), ByteBuffer.wrap(new byte[]{'3'}), ByteBuffer.wrap(new byte[]{'4'}), ByteBuffer.wrap(new byte[]{'5'}), ByteBuffer.wrap(new byte[]{'6'})), Flowable.error(new RuntimeException()) ); final ReactiveEntityProducer entityProducer = new ReactiveEntityProducer(publisher, CONTENT_LENGTH, CONTENT_TYPE, GZIP_CONTENT_ENCODING); final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024); final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel); entityProducer.produce(streamChannel); Assertions.assertEquals("12345", byteChannel.dump(StandardCharsets.US_ASCII)); final HttpStreamResetException exception = Assertions.assertThrows(HttpStreamResetException.class, () -> entityProducer.produce(streamChannel)); Assertions.assertTrue(exception.getCause() instanceof RuntimeException, "Expected published exception to be rethrown"); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); entityProducer.failed(exception); Assertions.assertEquals(1, entityProducer.available()); Assertions.assertTrue(byteChannel.isOpen()); Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII)); Assertions.assertFalse(entityProducer.isChunked()); Assertions.assertEquals(GZIP_CONTENT_ENCODING, entityProducer.getContentEncoding()); Assertions.assertNull(entityProducer.getTrailerNames()); Assertions.assertEquals(CONTENT_LENGTH, entityProducer.getContentLength()); Assertions.assertEquals(CONTENT_TYPE.toString(), entityProducer.getContentType()); Assertions.assertFalse(entityProducer.isRepeatable()); Assertions.assertEquals(1, entityProducer.available()); entityProducer.releaseResources(); } } httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/examples/0040775 0000000 0000000 00000000000 14403631147 025060 5ustar000000000 0000000 ././@LongLink0100644 0000000 0000000 00000000154 14403631147 011636 Lustar 0000000 0000000 httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/examples/ReactiveFullDuplexServerExample.javahttpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/examples/ReactiveFullDuplexServerExamp0100664 0000000 0000000 00000013006 14403631147 032731 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive.examples; import java.net.InetSocketAddress; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.BasicEntityDetails; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactive.ReactiveServerExchangeHandler; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.ListenerEndpoint; import org.apache.hc.core5.util.TimeValue; /** * Example of full-duplex HTTP/1.1 message exchanges using reactive streaming. This demo server works out-of-the-box * with {@link ReactiveFullDuplexClientExample}; it can also be invoked interactively using telnet. */ public class ReactiveFullDuplexServerExample { public static void main(final String[] args) throws Exception { int port = 8080; if (args.length >= 1) { port = Integer.parseInt(args[0]); } final IOReactorConfig config = IOReactorConfig.custom() .setSoTimeout(15, TimeUnit.SECONDS) .setTcpNoDelay(true) .build(); final HttpAsyncServer server = AsyncServerBootstrap.bootstrap() .setExceptionCallback(e -> e.printStackTrace()) .setIOReactorConfig(config) .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { System.out.println(connection.getRemoteAddress() + " " + new RequestLine(request)); } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { System.out.println(connection.getRemoteAddress() + " " + new StatusLine(response)); } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (keepAlive) { System.out.println(connection.getRemoteAddress() + " exchange completed (connection kept alive)"); } else { System.out.println(connection.getRemoteAddress() + " exchange completed (connection closed)"); } } }) .register("/echo", () -> new ReactiveServerExchangeHandler((request, entityDetails, responseChannel, context, requestBody, responseBodyFuture) -> { if (new BasicHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE).equals(request.getHeader(HttpHeaders.EXPECT))) { responseChannel.sendInformation(new BasicHttpResponse(100), context); } responseChannel.sendResponse( new BasicHttpResponse(200), new BasicEntityDetails(-1, ContentType.APPLICATION_OCTET_STREAM), context); // Simply using the request publisher as the response publisher will // cause the server to echo the request body. responseBodyFuture.execute(requestBody); })) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP server shutting down"); server.close(CloseMode.GRACEFUL); })); server.start(); final Future future = server.listen(new InetSocketAddress(port), URIScheme.HTTP); final ListenerEndpoint listenerEndpoint = future.get(); System.out.print("Listening on " + listenerEndpoint.getAddress()); server.awaitShutdown(TimeValue.ofDays(Long.MAX_VALUE)); } } ././@LongLink0100644 0000000 0000000 00000000154 14403631147 011636 Lustar 0000000 0000000 httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/examples/ReactiveFullDuplexClientExample.javahttpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/examples/ReactiveFullDuplexClientExamp0100664 0000000 0000000 00000013765 14403631147 032715 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive.examples; import static java.nio.charset.StandardCharsets.UTF_8; import java.net.URI; import java.nio.ByteBuffer; import java.util.Random; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.bootstrap.AsyncRequesterBootstrap; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactive.ReactiveEntityProducer; import org.apache.hc.core5.reactive.ReactiveResponseConsumer; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.Timeout; import org.reactivestreams.Publisher; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Observable; /** * Example of full-duplex HTTP/1.1 message exchanges using reactive streaming. This demo will stream randomly * generated text to the server via a POST request, while writing the response stream's events to standard output. * This demo works out-of-the-box with {@link ReactiveFullDuplexServerExample}. */ public class ReactiveFullDuplexClientExample { public static void main(final String[] args) throws Exception { String endpoint = "http://localhost:8080/echo"; if (args.length >= 1) { endpoint = args[0]; } // Create and start requester final HttpAsyncRequester requester = AsyncRequesterBootstrap.bootstrap() .setIOReactorConfig(IOReactorConfig.custom().setSoTimeout(5, TimeUnit.SECONDS).build()) .setStreamListener(new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { System.out.println(connection.getRemoteAddress() + " " + new RequestLine(request)); } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { System.out.println(connection.getRemoteAddress() + " " + new StatusLine(response)); } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (keepAlive) { System.out.println(connection.getRemoteAddress() + " exchange completed (connection kept alive)"); } else { System.out.println(connection.getRemoteAddress() + " exchange completed (connection closed)"); } } }) .create(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { System.out.println("HTTP requester shutting down"); requester.close(CloseMode.GRACEFUL); })); requester.start(); final Random random = new Random(); final Flowable publisher = Flowable.range(1, 100) .map(ignored -> { final String str = random.nextDouble() + "\n"; return ByteBuffer.wrap(str.getBytes(UTF_8)); }); final AsyncRequestProducer requestProducer = AsyncRequestBuilder.post(new URI(endpoint)) .setEntity(new ReactiveEntityProducer(publisher, -1, ContentType.TEXT_PLAIN, null)) .build(); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); final Future responseComplete = requester.execute(requestProducer, consumer, Timeout.ofSeconds(30), null); final Message> streamingResponse = consumer.getResponseFuture().get(); System.out.println(streamingResponse.getHead()); for (final Header header : streamingResponse.getHead().getHeaders()) { System.out.println(header); } System.out.println(); Observable.fromPublisher(streamingResponse.getBody()) .map(byteBuffer -> { final byte[] string = new byte[byteBuffer.remaining()]; byteBuffer.get(string); return new String(string); }) .materialize() .forEach(System.out::println); responseComplete.get(1, TimeUnit.MINUTES); System.out.println("Shutting down I/O reactor"); requester.initiateShutdown(); } } httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/WritableByteChannelMock.java0100664 0000000 0000000 00000007512 14245617503 030613 0ustar000000000 0000000 /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactive; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.WritableByteChannel; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; public class WritableByteChannelMock implements WritableByteChannel { private final int capacityLimit; private int capacityUsed; private ByteBuffer buf; private boolean closed; public WritableByteChannelMock(final int initialSize, final int capacityLimit) { this.buf = ByteBuffer.allocate(initialSize); this.capacityLimit = capacityLimit; } public WritableByteChannelMock(final int initialSize) { this(initialSize, 0); } private void expandCapacity(final int capacity) { final ByteBuffer oldbuffer = this.buf; this.buf = ByteBuffer.allocate(capacity); oldbuffer.flip(); this.buf.put(oldbuffer); } private void ensureCapacity(final int requiredCapacity) { if (requiredCapacity > this.buf.capacity()) { expandCapacity(requiredCapacity); } } @Override public int write(final ByteBuffer src) throws IOException { if (this.closed) { throw new ClosedChannelException(); } final int len = src.remaining(); ensureCapacity(this.buf.position() + len); if (this.capacityLimit > 0) { final int chunk = Math.min(this.capacityLimit - this.capacityUsed, len); if (chunk > 0) { final int limit = src.limit(); src.limit(src.position() + chunk); this.buf.put(src); src.limit(limit); this.capacityUsed += chunk; return chunk; } return 0; } this.buf.put(src); return len; } @Override public boolean isOpen() { return !this.closed; } @Override public void close() throws IOException { this.closed = true; } public void flush() { this.capacityUsed = 0; } public void reset() { this.capacityUsed = 0; this.buf.clear(); } public byte[] toByteArray() { final ByteBuffer dup = this.buf.duplicate(); dup.flip(); final byte[] bytes = new byte[dup.remaining()]; dup.get(bytes); return bytes; } public String dump(final Charset charset) throws CharacterCodingException { this.buf.flip(); final CharBuffer charBuffer = charset.newDecoder().decode(this.buf); this.buf.compact(); return charBuffer.toString(); } } httpcore5-reactive/pom.xml0100664 0000000 0000000 00000006010 14443065161 014642 0ustar000000000 0000000 httpcore5-parent org.apache.httpcomponents.core5 5.2.2 4.0.0 httpcore5-reactive Apache HttpComponents Core Reactive Extensions Apache HttpComponents Reactive Streams Bindings org.apache.httpcomponents.core5.httpcore5.reactive org.apache.httpcomponents.core5 httpcore5 org.reactivestreams reactive-streams 1.0.4 org.junit.jupiter junit-jupiter test io.reactivex.rxjava3 rxjava ${rxjava3.version} test maven-project-info-reports-plugin false index dependencies dependency-info summary