Dear Wiki user, You have subscribed to a wiki page or wiki category on "Httpcomponents Wiki" for change notification.
The following page has been changed by OlegKalnichevski: http://wiki.apache.org/HttpComponents/HttpCoreTutorial ------------------------------------------------------------------------------ * Identity coding - The end of the content entity is demarcated by closing the underying connection (EOF condition). For obvious reasons the identity encoing can only be used on the server side. Max entity length: unlimited. + The end of the content entity is demarcated by closing the underlying connection (EOF condition). For obvious reasons the identity encoding can only be used on the server side. Max entity length: unlimited. * Chunk coding @@ -449, +449 @@ === Terminating HTTP connections === HTTP connections can be terminated either gracefully by calling !HttpConnection#close() or forcibly by calling !HttpConnection#shutdown(). The former tries to flush all buffered data prior to terminating the connection and may block indefinitely. The !HttpConnection#close() method is not threading safe. The latter terminates the connection without flushing internal buffers and returns control to the caller as soon as possible without blocking for long. The !HttpConnection#shutdown() method is expected to be threading safe. + + == HTTP exception handling == + + All !HttpCore components throw two types of exceptions: !IOException in case of an I/O failure such as socket timeout or an socket reset and !HttpException that signals an HTTP failure such as a violation of the HTTP protocol. Usually I/O errors are considered non-fatal and recoverable, whereas HTTP protocol errors are considered fatal and cannot be automatically recovered from. + + === Protocol exception === + + !ProtocolException signals a fatal HTTP protocol violation that usually results in an immediate termination of the HTTP message processing. == HTTP protocol processors == + 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, transparent content compression / decompression being a good example. Usually this is accomplished by using the 'Decorator' pattern where a wrapper entity class is used to decorate the original entity. Several protocol interceptors can be combined to form one logical unit. + + 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. + + Protocol interceptors must be implemented threading safe. Similarly to servlets, protocol interceptors should not use instance variables unless access to those variables is synchronized. + - === Protocol interceptor === + === Standard protocol interceptors === + !HttpCore comes with a number of most essential protocol interceptors for client and server HTTP processing. - The chain of responsibility pattern. Each individual protocol interceptor is responsible - for implementing a particular aspect of the HTTP protocol - Description of the most important protocol interceptors + ==== RequestContent ==== + + !RequestContent is the most important interceptor for outgoing requests. It is responsible for delimiting content length by adding !Content-Length or !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. + ==== ResponseContent ==== + + !ResponseContent is the most important interceptor for outgoing responses. It is responsible for delimiting content length by adding !Content-Length or !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. + + ==== RequestConnControl ==== + + !RequestConnControl is responsible for adding !Connection header to the outgoing requests, which is essential for managing persistence of HTTP/1.0 connections. This interceptor is recommended for client side protocol processors. + + ==== ResponseConnControl ==== + + !ResponseConnControl is responsible for adding !Connection header to the outgoing responses, which is essential for managing persistence of HTTP/1.0 connections. This interceptor is recommended for server side protocol processors. + + ==== RequestDate ==== + + !RequestDate is responsible for adding !Date header to the outgoing requests This interceptor is optional for client side protocol processors. + + ==== ResponseDate ==== + + !ResponseDate is responsible for adding !Date header to the outgoing responses. This interceptor is recommended for server side protocol processors. + + ==== RequestExpectContinue ==== + + !RequestExpectContinue is responsible for enabling the 'expect-continue' handshake by adding !Expect header. This interceptor is recommended for client side protocol processors. + + ==== RequestTargetHost ==== + + !RequestTargetHost is responsible for adding !Host header. This interceptor is required for client side protocol processors. + + ==== RequestUserAgent ==== + + !RequestUserAgent is responsible for adding !User-Agent header. This interceptor is recommended for client side protocol processors. + + ==== ResponseServer ==== + + !ResponseServer is responsible for adding !Server header. This interceptor is recommended for server side protocol processors. + + === Working with protocol processors === + + Usually HTTP protocol processors are used to pre-process incoming messages prior to executing application specific processing logic and to post-process outgoing messages. + + {{{ + BasicHttpProcessor httpproc = new BasicHttpProcessor(); + // Required protocol interceptors + httpproc.addInterceptor(new RequestContent()); + httpproc.addInterceptor(new RequestTargetHost()); + // Recommended protocol interceptors + httpproc.addInterceptor(new RequestConnControl()); + httpproc.addInterceptor(new RequestUserAgent()); + httpproc.addInterceptor(new RequestExpectContinue()); + + HttpContext context = new BasicHttpContext(); + + HttpRequest request = new BasicHttpRequest("GET", "/"); + httpproc.process(request, context); + HttpResponse response = null; + }}} + Send the request to the target host and get a response. + {{{ + httpproc.process(response, context); + }}} + === HTTP context === + Protocol interceptors can collaborate by sharing information such as a processing state through an HTTP execution context. HTTP context is a structure that can be used to map an attribute name to an attribute value. Internally HTTP context implementations are usually backed by a HashMap. 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. - Protocol interceptors can collaborate by sharing information such as processing state - through the HTTP context - == HTTP protocol handlers == + {{{ + BasicHttpProcessor httpproc = new BasicHttpProcessor(); + httpproc.addInterceptor(new HttpRequestInterceptor() { - === Server side protocol handling === - - HttpService / HttpRequestHandler and friends - - === Client side protocol handling === - - HttpRequestExecutor and friends + public void process( + HttpRequest request, + HttpContext context) throws HttpException, IOException { + String id = (String) context.getAttribute("session-id"); + if (id != null) { + request.addHeader("Session-ID", id); + } - + } + + + }); + HttpRequest request = new BasicHttpRequest("GET", "/"); + httpproc.process(request, context); + }}} + + !HttpContexts can be linked together to form a hierarchy. In the simplest form one context can use content of another context to obtain default values of attributes not present in the local context. + + {{{ + HttpContext parentContext = new BasicHttpContext(); + parentContext.setAttribute("param1", Integer.valueOf(1)); + parentContext.setAttribute("param2", Integer.valueOf(2)); + + HttpContext localContext = new BasicHttpContext(); + localContext.setAttribute("param2", Integer.valueOf(0)); + localContext.setAttribute("param3", Integer.valueOf(3)); + HttpContext stack = new DefaultedHttpContext(localContext, parentContext); + + System.out.println(stack.getAttribute("param1")); + System.out.println(stack.getAttribute("param2")); + System.out.println(stack.getAttribute("param3")); + System.out.println(stack.getAttribute("param4")); + }}} + + stdout > + {{{ + 1 + 0 + 3 + null + }}} + + == HTTP parameters == + + !HttpParams represents a collection of immutable values that define a runtime behavior of a component. In many ways !HttpParams is similar to !HttpContext. The main distinction between the two lies in their use at runtime. Both interfaces represent a collection of objects that are organized as a map of textual names to object values, but serve distinct purposes: + + * !HttpParams is intended to contain simple objects: integers, doubles, strings, collections and objects that remain immutable at runtime. !HttpParams is expected to be used in the 'write once - ready many' mode. !HttpContext is intended to contain complex objects that are very likely to mutate in the course of HTTP message processing. + + * The purpose of !HttpParams is to define a behavior of other components. Usually each complex component has its own !HttpParams object. The purpose of !HttpContext is to represent an execution state of an HTTP process. Usually the same execution context is shared among many collaborating objects. + + !HttpParams, like !HttpContext, can be linked together to form a hierarchy. In the simplest form one set of parameters can use content of another one to obtain default values of parameters not present in the local set. + + {{{ + HttpParams parentParams = new BasicHttpParams(); + parentParams.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_0); + parentParams.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8"); + + HttpParams localParams = new BasicHttpParams(); + localParams.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1); + localParams.setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, Boolean.FALSE); + HttpParams stack = new DefaultedHttpParams(localParams, parentParams); + + System.out.println(stack.getParameter(CoreProtocolPNames.PROTOCOL_VERSION)); + System.out.println(stack.getParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET)); + System.out.println(stack.getParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE)); + System.out.println(stack.getParameter(CoreProtocolPNames.USER_AGENT)); + }}} + + stdout > + {{{ + HTTP/1.1 + UTF-8 + false + null + }}} + + === HTTP parameter beans === + + !HttpParams interface allows for a great deal of flexibility in handling configuration of components. Most importantly, new parameters can be introduced without affecting binary compatibility with older versions. However, !HttpParams also has a certain disadvantage compared to regular java beans: !HttpParams cannot be assembled using a DI framework. To mitigate the limitation, !HttpCore includes a number of bean classes that cab used in order to initialize !HttpParams objects using standard java bean conventions. + + {{{ + HttpParams params = new BasicHttpParams(); + HttpProtocolParamBean paramsBean = new HttpProtocolParamBean(params); + paramsBean.setVersion(HttpVersion.HTTP_1_1); + paramsBean.setContentCharset("UTF-8"); + paramsBean.setUseExpectContinue(true); + + System.out.println(params.getParameter(CoreProtocolPNames.PROTOCOL_VERSION)); + System.out.println(params.getParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET)); + System.out.println(params.getParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE)); + System.out.println(params.getParameter(CoreProtocolPNames.USER_AGENT)); + }}} + + stdout > + {{{ + HTTP/1.1 + UTF-8 + false + null + }}} + + == Blocking HTTP protocol handlers == + + === HTTP service === + + !HttpService is a server side HTTP protocol handler based in the blocking I/O model that implements the essential requirements of the HTTP protocol for the server side message processing as described by RFC 2616. !HttpService relies on !HttpProcessor to take care of the cross-cutting protocol aspects that apply to all incoming and outgoing messages. + + {{{ + HttpParams params; + // Initialize HTTP parameters + HttpProcessor httpproc; + // Initialize HTTP processor + + HttpService httpService = new HttpService( + httpproc, + new DefaultConnectionReuseStrategy(), + new DefaultHttpResponseFactory()); + httpService.setParams(params); + }}} + + ==== HTTP request handlers ==== + + !HttpRequestHandler interface represents a routine intended to handle processing of a specific group of HTTP requests. !HttpService is designed to take care of protocol specific aspects, whereas request handlers take care of application specific HTTP processing. Their main purpose is to generate a content entity to be send back to the client in response to the given request. + + {{{ + HttpRequestHandler myRequestHandler = new HttpRequestHandler() { + + public void handle( + HttpRequest request, + HttpResponse response, + HttpContext context) throws HttpException, IOException { + response.setStatusCode(HttpStatus.SC_OK); + response.addHeader("Content-Type", "text/plain"); + response.setEntity(new StringEntity("some important message")); + } + + }); + }}} + + Request handlers must be implemented threading safe. Similarly to servlets, request handlers should not use instance variables unless access to those variables is synchronized. + + ==== Request handler resolver ==== + + HTTP request handlers are usually managed by a !HttpRequestHandlerResolver that matches a request URI to a request handler. !HttpCore includes an over-simplified implementation of request handler resolver based on a trivial pattern matching algorithm: !HttpRequestHandlerRegistry supports only three formats: *, <uri>* and *<uri>. + + {{{ + HttpService httpService; + // Initialize HTTP service + + HttpRequestHandlerRegistry handlerResolver = new HttpRequestHandlerRegistry(); + handlerReqistry.register("/service/*", myRequestHandler1); + handlerReqistry.register("*.do", myRequestHandler2); + handlerReqistry.register("*", myRequestHandler3); + + // Inject handler resolver + httpService.setHandlerResolver(handlerResolver); + }}} + + Users are encouraged to provide more sophisticated implementations of !HttpRequestHandlerResolver, for instance, based on regular expressions. + + ==== Using HTTP service to handle requests ==== + + When fully initialized and configured !HttpService can be used to execute handle requests for active HTTP connections. !HttpService#!handleRequest() method reads an incoming requests, generates a response and sends it back to the client. This method can be executed in a loop to handle multiple requests on a persistent connection. The !HttpService#!handleRequest() is safe to execute from multiple threads to process requests on several connections simultaneously as long as protocol interceptors and requests handlers used by the !HttpService are threading safe. + + {{{ + HttpService httpService; + // Initialize HTTP service + HttpServerConnection conn; + // Initialize connection + HttpContext context; + // Initialize HTTP context + + boolean active = true; + try { + while (active && conn.isOpen()) { + httpService.handleRequest(conn, context); + } + } finally { + conn.shutdown(); + } + }}} + + === HTTP request executor === + + !HttpRequestExecutor is a client side HTTP protocol handler based on the blocking I/O model that implements the essential requirements of the HTTP protocol for the client side message processing as described by RFC 2616. !HttpRequestExecutor, similarly to its server side counterpart, makes use of !HttpProcessor to take care of the cross-cutting protocol aspects that apply to all incoming and outgoing messages. Application specific processing can be implemented outside !HttpRequestExecutor once the request has been executed and a response has been received. + + {{{ + HttpClientConnection conn; + // Create connection + HttpParams params; + // Initialize HTTP parameters + HttpProcessor httpproc; + // Initialize HTTP processor + HttpContext context; + // Initialize HTTP context + + HttpRequestExecutor httpexecutor = new HttpRequestExecutor(); + + BasicHttpRequest request = new BasicHttpRequest("GET", "/"); + request.setParams(params); + httpexecutor.preProcess(request, httpproc, context); + HttpResponse response = httpexecutor.execute(request, conn, context); + response.setParams(params); + httpexecutor.postProcess(response, httpproc, context); + + HttpEntity entity = response.getEntity(); + if (entity != null) { + entity.consumeContent(); + } + }}} + + Methods of !HttpRequestExecutor are safe to execute from multiple threads to execute requests on several connections simultaneously as long as protocol interceptors used by the !HttpRequestExecutor are threading safe. + === Connection persistence / re-use === + !ConnectionReuseStrategy interface is intended to determine whether the underlying connection can be reused for processing of consecutive messages after the transmission of the current one has been completed. The default connection re-use strategy attempts to keep connection alive whenever possible. Firstly, it examines the version of the HTTP protocol used to transmit the message. HTTP/1.1 connections are persistent per default, HTTP/1.0 are not. Secondly, it examines the value of the !Connection header. The peer can indicate whether it intends to re-use the connection on the opposite side by sending !Keep-Alive or !Close values in the !Connection header. Thirdly, the strategy makes the decision whether the connection is safe to re-use based on the properties of the enclosed entity, if available. - ConnectionReuseStrategy interface and its implementations - - == HTTP parameters == - - HttpParams interface; Parameter hierachies - === HTTP parameter beans === - - Using plains beans for assembling HTTP parameters - - == Customization of the HTTP message parsing / formatting == - - LineParser / LineFormatter interfaces - - HeaderValueParser / HeaderValueFormatter interfaces - - == Example of building a simple HTTP server (using blocking I/O model) == - - Code sample with a code walk-through - - == Example of building a simple HTTP client (using blocking I/O model) == - - Code sample with a code walk-through - = NIO extensions = == Benefits and shortcomings of the non-blocking I/O model == - NIO does not necessarily fit all use cases. Use only where appropriate: + Contrary to the popular belief, the performance of NIO in terms of raw data throughput is significantly lower than than of the blocking I/O. NIO does not necessarily fit all use cases and should be used only where appropriate: + * handling of thousands of connections, significant number of which can be idle + - * lots high latency connections + * handling high latency connections - * request / response handling needs to be decoupled. + * request / response handling needs to be decoupled. == Differences from other NIO frameworks == - Solves similar problems as other frameworks, but has certain distinct + Solves similar problems as other frameworks, but has certain distinct features - features - * minimalistic, optimized for data volume intensive protocols such as HTTP + * minimalistic, optimized for data volume intensive protocols such as HTTP - * efficient memory management + * efficient memory management: data consumer can read only as much input data as it can process without having to allocate more memory - * direct access to the NIO channels where possible + * direct access to the NIO channels where possible + + == I/O reactor == + + !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 one thread per connection model imposed by the classic blocking I/O model. !IOReactor interface represents an abstract object implementing the Reactor pattern. Internally, !IOReactor implementations encapsulate functionality of the NIO !Selector. + + I/O reactors usually employ a small number of worker 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. + + {{{ + HttpParams params = new BasicHttpParams(); + int workerCount = 2; + IOReactor ioreactor = new DefaultConnectingIOReactor(workerCount, params); + }}} + + === I/O dispatchers === + + !IOReactor implementations make use of the !IOEventDispatch interface to notify of events pending for a particular session. All methods of the !IOEventDispatch are executed on a dispatch thread of the I/O reactor. Therefore, it is important that processing that takes place in the event methods will not block the dispatch thread for too long, as the I/O reactor will be unable to react to other events. + + {{{ + HttpParams params = new BasicHttpParams(); + IOReactor ioreactor = new DefaultConnectingIOReactor(2, params); - + + IOEventDispatch eventDispatch = new MyIOEventDispatch(); + ioreactor.execute(eventDispatch); + }}} + + There are several events methods of the !IOEventDispatch + + * connected: new session has been created + + * inputReady: session has pending input + + * outputReady: session is ready for output + + * timeout: session timed out + + * disconnected: session has been terminated + - == I/O reactor pattern == + === I/O reactor shutdown === - HttpCore NIO is based on the Reactor pattern as described by Doug Lea + The shutdown of I/O reactors is a complex process and may usually take a while to complete. I/O reactors will attempt to gracefully terminate all active I/O sessions and dispatch threads approximately within the specified grace period. If any of the I/O sessions fails to terminate correctly, the I/O reactor will forcibly shut down remaining sessions. + {{{ + int gracePeriod = 3000; // milliseconds + ioreactor.shutdown(gracePeriod); + }}} + + The !IOReactor#shutdown(long) method is safe to call from any thread. + - === I/O session === + === I/O sessions === + I/O session represents a sequence of logically related data exchanges between two end points. I/O session encapsulates functionality of NIO !SelectionKey and !SocketChannel. One can use the channel associated with the I/O session to read from and data from it. - IOSession interface and friends - - === I/O event dispatch === - IOEventDispatch interface - + {{{ + IOSession iosession; + ReadableByteChannel channel = (ReadableByteChannel) iosession.channel(); + ByteBuffer dst = ByteBuffer.allocate(2048); + channel.read(dst); + }}} + + === I/O session state management === + + 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. + + {{{ + IOSession iosession; + Object someState; + iosession.setAttribute("state", someState); + Object currentState = iosession.getAttribute("state"); + }}} + + Please note if several sessions make use of shared objects, access to those objects must be synchronized or threading safe. + + === I/O session event mask === + + One can declare an interest in a particular type of I/O events for a particular I/O session by setting its event mask. + + {{{ + IOSession iosession; + iosession.setEventMask(SelectionKey.OP_READ | SelectionKey.OP_WRITE); + }}} + + One can also toggle !OP_READ and !OP_WRITE flags individually. + + {{{ + iosession.setEvent(SelectionKey.OP_READ); + iosession.clearEvent(SelectionKey.OP_READ); + }}} + + Event notifications will not take place if the corresponding interest flag is not set. + + === I/O session buffers === + + Quite often I/O sessions need to maintain internal I/O buffers in order to transform input / output data prior to returning it to the consumer or writing it to the underlying channel. Memory management in !HttpCore NIO is based on the fundamental principle that the data consumer can read only as much input data as it can process without having to allocate more memory. That means that quite often some input data may remain unread in one of the internal or external session buffers. The I/O reactor can query the status of the session buffers and make sure the consumer gets correctly notified of more data stored in one of the session buffers, thus allowing the consumer to read the remaining data once it is able to process it. I/O sessions can be made aware of the status of external session buffers using the !SessionBufferStatus interface. + + {{{ + IOSession iosession; + iosession.hasBufferedInput(); + iosession.hasBufferedOutput(); + SessionBufferStatus myBufferStatus = new MySessionBufferStatus(); + iosession.setBufferStatus(myBufferStatus); + }}} + + === I/O session shutdown === + + One can close an I/O session gracefully by calling !IOSession#close() allowing the session to be closed in an orderly manner or by calling !IOSession#shutdown() to forcibly close the underlying channel. The distinction between two methods is of primary importance for those types of I/O sessions that involve some sort of a session termination handshake such as SSL/TLS connections. + === Listening I/O reactors === ListeningIOReactor and ListenerEndpoint interfaces @@ -541, +894 @@ ConnectingIOReactor, SessionRequest and SessionRequestCallback interfaces - == NHTTP connections == + == Non-blocking HTTP connections == + Effectively NHTTP connections are wrappers around !IOSession with HTTP specific functionality. NHTTP connections are stateful and not threading safe. Input / output operations on NHTTP connections should be restricted to the dispatch events triggered by the I/O event dispatch thread. - NHttpClientConnection, NHttpServerConnection interfaces and friends. Basically - wrappers around IOSession; - - NHTTP connections are stateful. They should be read from / written to - from the I/O event dispatch thread. - - == NHTTP message serialization and deserialization == - NHttpMessageWriter / NHttpMessageParser interfaces + === Execution context of non-blocking HTTP connections === - === I/O control === + NHTTP connections are not bound to a particular thread of execution and therefore they need to maintain their own execution context. Each NHTTP connection has an !HttpContext instance associated with it, which can be used to maintain a processing state. The !HttpContext instance is threading safe and can be manipulated from multiple threads. - IOControl interface; suspending / requesting I/O event notifications + {{{ + // Get NHTTP connection + DefaultNHttpClientConnection conn; + // State + Object myStateObject; + + HttpContext context = conn.getContext(); + context.setAttribute("state", myStateObject); + }}} + + === Interacting with non-blocking HTTP connections === + + All NHTTP connections classes implement !IOControl interface, which represents a subset of connection functionality for controlling interest in I/O even notifications. !IOControl instances are expected to be fully threading safe. Therefore !IOControl can be used to request / suspend I/O event notifications from any thread. + + One must take special precautions when interacting with NHTTP connections. Input / output operations on a NHTTP connection may lead to unpredictable results if executed from any thread other than the I/O event dispatch thread. + + The following pattern is recommended: + + * Use !IOControl interface to pass control over connection's I/O events to another thread / session. + + * If input / output operations need be executed on that particular connection, store all the required information (state) in the connection context and request the appropriate I/O operation by calling !IOControl#requestInput() or !IOControl#requestOutput() method + + * Execute the required operations from the event method on the dispatch thread using information stored in connection context. + + Please note all operations that take place in the event methods should not block for too long, because while the dispatch thread remains blocked in one session, it is unable to process events for all other sessions. I/O operations with the underlying channel of the session are not a problem as they are guaranteed to be non-blocking. + + === Content codecs === + + ContentDecoder and ContentEncoder interfaces + === Direct transfer to and from file channels === + + FileContentDecoder and FileContentEncoder interfaces + == NHTTP entities == Producing entities; consuming entities; - === Content codecs === - - ContentDecoder and ContentEncoder interfaces - - === Direct transfer to and from file channels === - - FileContentDecoder and FileContentEncoder interfaces - == NHTTP protocol handlers == === Asynchronous protocol handlers === @@ -591, +962 @@ SSLIOSession, SSLServerIOEventDispatch, SSLClientIOEventDispatch classes + + == Customization of the HTTP message parsing / formatting == + + LineParser / LineFormatter interfaces + + HeaderValueParser / HeaderValueFormatter interfaces + + == Example of building a simple HTTP server (using blocking I/O model) == + + Code sample with a code walk-through + + == Example of building a simple HTTP client (using blocking I/O model) == + + Code sample with a code walk-through + == Example of building a simple HTTP server (using non-blocking I/O model) == Code sample with a code walk-through --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
