diff options
Diffstat (limited to 'simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ResponseBuilder.java')
-rw-r--r-- | simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ResponseBuilder.java | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ResponseBuilder.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ResponseBuilder.java new file mode 100644 index 0000000..0ba780e --- /dev/null +++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ResponseBuilder.java @@ -0,0 +1,159 @@ +/* + * ResponseBuilder.java February 2014 + * + * Copyright (C) 2014, Niall Gallagher <niallg@users.sf.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package org.simpleframework.http.socket.service; + +import static org.simpleframework.http.Protocol.CLOSE; +import static org.simpleframework.http.Protocol.CONNECTION; +import static org.simpleframework.http.Protocol.DATE; +import static org.simpleframework.http.Protocol.SEC_WEBSOCKET_ACCEPT; +import static org.simpleframework.http.Protocol.UPGRADE; +import static org.simpleframework.http.Protocol.WEBSOCKET; +import static org.simpleframework.http.socket.service.ServiceEvent.WRITE_HEADER; + +import java.io.IOException; + +import org.simpleframework.http.Request; +import org.simpleframework.http.Response; +import org.simpleframework.http.Status; +import org.simpleframework.transport.Channel; +import org.simpleframework.transport.ByteWriter; +import org.simpleframework.transport.trace.Trace; + +/** + * The <code>ResponseBuilder</code> object is used to build a response + * to a WebSocket handshake. In order for a successful handshake to + * complete a HTTP request must have a version of 13 referring + * to RFC 6455, a WebSocket key, and the required HTTP connection + * details. If any of these are missing the server is obliged to + * respond with a HTTP 400 response indicating a bad request. + * + * @author Niall Gallagher + */ +class ResponseBuilder { + + /** + * This is used to validate the initiating WebSocket request. + */ + private final RequestValidator validator; + + /** + * This is the accept token generated for the request. + */ + private final AcceptToken token; + + /** + * This is the sender used to send the WebSocket response. + */ + private final ByteWriter writer; + + /** + * This is the response to the WebSocket handshake. + */ + private final Response response; + + /** + * This is the underlying TCP channel for the request. + */ + private final Channel channel; + + /** + * This is used to trace the activity for the handshake. + */ + private final Trace trace; + + /** + * Constructor for the <code>ResponseBuilder</code> object. In order + * to process the WebSocket handshake this requires the original + * request and the response as well as the underlying TCP channel + * which forms the basis of the WebSocket connection. + * + * @param request this is the request that initiated the handshake + * @param response this is the response for the handshake + */ + public ResponseBuilder(Request request, Response response) throws Exception { + this.validator = new RequestValidator(request); + this.token = new AcceptToken(request); + this.channel = request.getChannel(); + this.writer = channel.getWriter(); + this.trace = channel.getTrace(); + this.response = response; + } + + /** + * This is used to determine if the client handshake request had + * all the required headers as dictated by RFC 6455 section 4.2.1. + * If the request does not contain any of these parts then this + * will return false, indicating a HTTP 400 response is sent to + * the client, otherwise a HTTP 101 response is sent. + */ + public void commit() throws IOException { + if(validator.isValid()) { + accept(); + } else { + reject(); + } + } + + /** + * This is used to respond to the client with a HTTP 400 response + * indicating the WebSocket handshake failed. No response body is + * sent with the rejection message and the underlying TCP channel + * is closed to prevent further use of the connection. + */ + private void reject() throws IOException { + long time = System.currentTimeMillis(); + + response.setStatus(Status.BAD_REQUEST); + response.setValue(CONNECTION, CLOSE); + response.setDate(DATE, time); + + String header = response.toString(); + byte[] message = header.getBytes("UTF-8"); + + trace.trace(WRITE_HEADER, header); + writer.write(message); + writer.flush(); + writer.close(); + } + + /** + * This is used to respond to the client with a HTTP 101 response + * to indicate that the WebSocket handshake succeeeded. Once this + * response has been sent all traffic between the client and + * server will be with WebSocket frames as defined by RFC 6455. + */ + private void accept() throws IOException { + long time = System.currentTimeMillis(); + String accept = token.create(); + + response.setStatus(Status.SWITCHING_PROTOCOLS); + response.setDescription(UPGRADE); + response.setValue(CONNECTION, UPGRADE); + response.setDate(DATE, time); + response.setValue(SEC_WEBSOCKET_ACCEPT, accept); + response.setValue(UPGRADE, WEBSOCKET); + + String header = response.toString(); + byte[] message = header.getBytes("UTF-8"); + + trace.trace(WRITE_HEADER, header); + writer.write(message); + writer.flush(); + } +} |