All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.generallycloud.baseio.codec.http11.future.AbstractHttpReadFuture Maven / Gradle / Ivy
/*
* Copyright 2015-2017 GenerallyCloud.com
*
* 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 com.generallycloud.baseio.codec.http11.future;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.generallycloud.baseio.buffer.ByteBuf;
import com.generallycloud.baseio.codec.http11.WebSocketProtocolFactory;
import com.generallycloud.baseio.common.BASE64Util;
import com.generallycloud.baseio.common.KMPByteUtil;
import com.generallycloud.baseio.common.KMPUtil;
import com.generallycloud.baseio.common.ReleaseUtil;
import com.generallycloud.baseio.common.SHA1Util;
import com.generallycloud.baseio.common.StringLexer;
import com.generallycloud.baseio.common.StringUtil;
import com.generallycloud.baseio.component.ByteArrayBuffer;
import com.generallycloud.baseio.component.MapParameters;
import com.generallycloud.baseio.component.Parameters;
import com.generallycloud.baseio.component.SocketChannelContext;
import com.generallycloud.baseio.component.SocketSession;
import com.generallycloud.baseio.protocol.AbstractChannelReadFuture;
import com.generallycloud.baseio.protocol.ChannelReadFuture;
import com.generallycloud.baseio.protocol.ProtocolDecoder;
import com.generallycloud.baseio.protocol.ProtocolEncoder;
//FIXME 改进header parser
/**
*
* Content-Type: application/x-www-form-urlencoded Content-Type:
* multipart/form-data; boundary=----WebKitFormBoundaryKA6dsRskWA4CdJek
*
*/
public abstract class AbstractHttpReadFuture extends AbstractChannelReadFuture implements HttpReadFuture {
protected static final KMPUtil KMP_BOUNDARY = new KMPUtil("boundary=");
protected static final KMPByteUtil KMP_HEADER = new KMPByteUtil("\r\n\r\n".getBytes());
protected static final WebSocketProtocolFactory WS_PROTOCOL_FACTORY = new WebSocketProtocolFactory();
protected static final ProtocolDecoder WS_PROTOCOL_DECODER = WS_PROTOCOL_FACTORY.getProtocolDecoder();
protected static final ProtocolEncoder WS_PROTOCOL_ENCODER = WS_PROTOCOL_FACTORY.getProtocolEncoder();
protected ByteArrayBuffer binaryBuffer;
protected boolean body_complete;
protected byte [] bodyArray;
protected ByteBuf bodyContent;
protected int bodyLimit;
protected String boundary;
protected int contentLength;
protected String contentType;
protected List cookieList;
protected Map cookies;
protected StringBuilder currentHeaderLine;
protected boolean hasBodyContent;
protected boolean header_complete;
protected int headerLength;
protected int headerLimit;
protected List headerLines;;
protected String host;
protected String method;
protected Map params;
protected Map request_headers;
protected String requestURI;
protected String requestURL;
protected Map response_headers;
protected SocketSession session;
protected HttpStatus status = HttpStatus.C200;
protected String version;
private MapParameters mapParameters;
private boolean updateWebSocketProtocol;
public AbstractHttpReadFuture(SocketChannelContext context) {
super(context);
}
public AbstractHttpReadFuture(SocketSession session, ByteBuf readBuffer,int headerLimit,int bodyLimit) {
super(session.getContext());
this.session = session;
this.headerLimit = headerLimit;
this.bodyLimit = bodyLimit;
this.cookies = new HashMap();
this.request_headers = new HashMap();
this.headerLines = new ArrayList();
this.currentHeaderLine = new StringBuilder();
}
@Override
public void addCookie(Cookie cookie) {
if (cookieList == null) {
cookieList = new ArrayList();
}
cookieList.add(cookie);
}
protected void doBodyComplete() {
body_complete = true;
bodyContent.flip();
bodyArray = bodyContent.getBytes();
if (CONTENT_APPLICATION_URLENCODED.equals(contentType)) {
// FIXME encoding
String paramString = new String(bodyArray, session.getEncoding());
parseParamString(paramString);
this.readText = paramString;
} else {
// FIXME 解析BODY中的内容
}
}
public void doHeaderComplete(SocketSession session,List headerLines) {
parseFirstLine(headerLines.get(0));
for (int i = 1; i < headerLines.size(); i++) {
String l = headerLines.get(i);
int p = l.indexOf(":");
if (p == -1) {
setRequestHeader(l, null);
continue;
}
String name = l.substring(0, p).trim();
String value = l.substring(p + 1).trim();
setRequestHeader(name, value);
}
host = getRequestHeader("Host");
String _contentLength = getRequestHeader(HttpHeader.CONTENT_LENGTH);
int contentLength = 0;
if (!StringUtil.isNullOrBlank(_contentLength)) {
contentLength = Integer.parseInt(_contentLength);
this.contentLength = contentLength;
}
String contentType = getRequestHeader(HttpHeader.CONTENT_TYPE);
parseContentType(contentType);
String cookie = getRequestHeader("Cookie");
if (!StringUtil.isNullOrBlank(cookie)) {
parse_cookies(cookie, cookies);
}
if (contentLength < 1) {
body_complete = true;
return ;
}
hasBodyContent = true;
// FIXME 写入临时文件
bodyContent = allocate(session, contentLength,bodyLimit);
}
@Override
public ChannelReadFuture flush() {
if (updateWebSocketProtocol) {
session.setProtocolDecoder(WS_PROTOCOL_DECODER);
session.setProtocolEncoder(WS_PROTOCOL_ENCODER);
session.setProtocolFactory(WS_PROTOCOL_FACTORY);
session.setAttribute(WebSocketReadFuture.SESSION_KEY_SERVICE_NAME, getFutureName());
}
return super.flush();
}
@Override
public ByteArrayBuffer getBinaryBuffer() {
return binaryBuffer;
}
@Override
public byte[] getBodyContent() {
return bodyArray;
}
@Override
public String getBoundary() {
return boundary;
}
@Override
public int getContentLength() {
return contentLength;
}
@Override
public String getContentType() {
return contentType;
}
@Override
public String getCookie(String name) {
return cookies.get(name);
}
@Override
public List getCookieList() {
return cookieList;
}
@Override
public String getFutureName() {
return getRequestURI();
}
@Override
public String getHost() {
return host;
}
@Override
public String getMethod() {
return method;
}
@Override
public Parameters getParameters() {
if (mapParameters == null) {
mapParameters = new MapParameters(getRequestParams());
}
return mapParameters;
}
@Override
public String getRequestHeader(String name) {
if (StringUtil.isNullOrBlank(name)) {
return null;
}
// return request_headers.get(name.toLowerCase());
return request_headers.get(name);
}
@Override
public Map getRequestHeaders() {
return request_headers;
}
@Override
public String getRequestParam(String key) {
return params.get(key);
}
@Override
public Map getRequestParams() {
return params;
}
@Override
public String getRequestURI() {
return requestURI;
}
@Override
public String getRequestURL() {
return requestURL;
}
@Override
public Map getResponseHeaders() {
if (response_headers == null) {
response_headers = new HashMap();
setDefaultResponseHeaders(response_headers);
}
return response_headers;
}
@Override
public HttpStatus getStatus() {
return status;
}
@Override
public String getVersion() {
return version;
}
@Override
public boolean hasBodyContent() {
return hasBodyContent;
}
private void parse_cookies(String line, Map cookies) {
StringLexer l = new StringLexer(0, line.toCharArray());
StringBuilder value = new StringBuilder();
String k = null;
String v = null;
boolean findKey = true;
for (;;) {
char c = l.current();
switch (c) {
case ' ':
break;
case '=':
if (!findKey) {
throw new IllegalArgumentException();
}
k = value.toString();
value = new StringBuilder();
findKey = false;
break;
case ';':
if (findKey) {
throw new IllegalArgumentException();
}
findKey = true;
v = value.toString();
value = new StringBuilder();
cookies.put(k, v);
break;
default:
value.append(c);
break;
}
if (!l.next()) {
break;
}
}
cookies.put(k, value.toString());
}
protected abstract void parseContentType(String contentType);
protected abstract void parseFirstLine(String line);
protected void parseParamString(String paramString) {
String[] array = paramString.split("&");
for (String s : array) {
if (StringUtil.isNullOrBlank(s)) {
continue;
}
String[] unitArray = s.split("=");
if (unitArray.length != 2) {
continue;
}
String key = unitArray[0];
String value = unitArray[1];
params.put(key, value);
}
}
private void readHeader(ByteBuf buffer) throws IOException{
for (; buffer.hasRemaining();) {
if (++headerLength > headerLimit) {
throw new IOException("max http header length " + headerLimit);
}
byte b = buffer.getByte();
if (b == '\n') {
if (currentHeaderLine.length() == 0) {
header_complete = true;
break;
} else {
headerLines.add(currentHeaderLine.toString());
currentHeaderLine.setLength(0);
}
continue;
} else if (b == '\r') {
continue;
} else {
currentHeaderLine.append((char) b);
}
}
}
@Override
public boolean read(SocketSession session, ByteBuf buffer) throws IOException {
if (!header_complete) {
readHeader(buffer);
if (!header_complete) {
return false;
}
doHeaderComplete(session,headerLines);
}
if (!body_complete) {
bodyContent.read(buffer);
if (bodyContent.hasRemaining()) {
return false;
}
doBodyComplete();
}
return true;
}
@Override
public void release() {
ReleaseUtil.release(bodyContent);
}
protected abstract void setDefaultResponseHeaders(Map headers);
@Override
public void setRequestHeader(String name, String value) {
if (request_headers == null) {
throw new RuntimeException("did you want to set response header ?");
}
if (StringUtil.isNullOrBlank(name)) {
return;
}
// request_headers.put(name.toLowerCase(), value);
request_headers.put(name, value);
}
@Override
public void setRequestHeaders(Map headers) {
this.request_headers = headers;
}
@Override
public void setRequestParams(Map params) {
this.params = params;
}
@Override
public void setRequestURL(String url) {
this.requestURL = url;
int index = url.indexOf("?");
if (index > -1) {
String paramString = url.substring(index + 1, url.length());
parseParamString(paramString);
requestURI = url.substring(0, index);
} else {
this.requestURI = url;
}
}
@Override
public void setResponseHeader(String name, String value) {
if (response_headers == null) {
response_headers = new HashMap();
setDefaultResponseHeaders(response_headers);
}
response_headers.put(name, value);
}
@Override
public void setResponseHeaders(Map headers) {
this.response_headers = headers;
}
@Override
public void setReuestParam(String key, String value) {
if (params == null) {
params = new HashMap();
}
this.params.put(key, value);
}
@Override
public void setStatus(HttpStatus status) {
this.status = status;
}
@Override
public void updateWebSocketProtocol() {
String Sec_WebSocket_Key = getRequestHeader("Sec-WebSocket-Key");
if (!StringUtil.isNullOrBlank(Sec_WebSocket_Key)) {
//FIXME 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 必须这个值?
String Sec_WebSocket_Key_Magic = Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
byte[] key_array = SHA1Util.SHA1(Sec_WebSocket_Key_Magic);
String acceptKey = BASE64Util.byteArrayToBase64(key_array);
setStatus(HttpStatus.C101);
setResponseHeader("Connection", "Upgrade");
setResponseHeader("Upgrade", "WebSocket");
setResponseHeader("Sec-WebSocket-Accept", acceptKey);
updateWebSocketProtocol = true;
return;
}
throw new IllegalArgumentException("illegal http header : empty Sec-WebSocket-Key");
}
@Override
public void writeBinary(byte[] binary) {
if (binaryBuffer == null) {
binaryBuffer = new ByteArrayBuffer(binary);
return;
}
binaryBuffer.write(binary);
}
}