From d87c31c23c12e0e8ca81cb99b2431e60227c3f45 Mon Sep 17 00:00:00 2001 From: funa-tk <1781263+funa-tk@users.noreply.github.com> Date: Mon, 16 Dec 2019 01:36:32 +0900 Subject: [PATCH 1/2] add streaming response on HTTPv1 --- .../packetproxy/common/FilterTextParser.java | 7 +- .../packetproxy/encode/EncodeHTTPBase.java | 6 +- .../encode/EncodeHTTPStreamingResponse.java | 66 +++++---- .../java/core/packetproxy/gui/GUIOption.java | 6 + .../core/packetproxy/gui/GUIOptionHttp.java | 61 ++++++++ src/main/java/core/packetproxy/http/Http.java | 31 +++- .../java/core/packetproxy/http/Https.java | 13 +- .../http1/Http1StreamingResponse.java | 93 ++++++++++++ .../java/core/packetproxy/http2/HTTP2.java | 13 +- .../java/core/packetproxy/http2/Http2.java | 132 ++++++++++++++++++ ...ponse.java => Http2StreamingResponse.java} | 6 +- 11 files changed, 385 insertions(+), 49 deletions(-) create mode 100644 src/main/java/core/packetproxy/gui/GUIOptionHttp.java create mode 100644 src/main/java/core/packetproxy/http1/Http1StreamingResponse.java create mode 100644 src/main/java/core/packetproxy/http2/Http2.java rename src/main/java/core/packetproxy/http2/{StreamingResponse.java => Http2StreamingResponse.java} (98%) diff --git a/src/main/java/core/packetproxy/common/FilterTextParser.java b/src/main/java/core/packetproxy/common/FilterTextParser.java index 1f436d4..52af459 100644 --- a/src/main/java/core/packetproxy/common/FilterTextParser.java +++ b/src/main/java/core/packetproxy/common/FilterTextParser.java @@ -62,9 +62,10 @@ public class FilterTextParser { { put("modified", 10); } { put("type", 11); } { put("encode", 12); } - { put("group", 13); } - { put("full_text", 14); } - { put("full_text_i", 15); } + { put("alpn", 13); } + { put("group", 14); } + { put("full_text", 15); } + { put("full_text_i", 16); } }; FilterTextParser(String str) { diff --git a/src/main/java/core/packetproxy/encode/EncodeHTTPBase.java b/src/main/java/core/packetproxy/encode/EncodeHTTPBase.java index e2f9df0..a1017d3 100644 --- a/src/main/java/core/packetproxy/encode/EncodeHTTPBase.java +++ b/src/main/java/core/packetproxy/encode/EncodeHTTPBase.java @@ -18,7 +18,7 @@ import java.io.InputStream; import packetproxy.http.Http; -import packetproxy.http2.HTTP2; +import packetproxy.http2.Http2; import packetproxy.model.Packet; public abstract class EncodeHTTPBase extends Encoder @@ -27,7 +27,7 @@ public enum HTTPVersion { HTTP1, HTTP2 } private HTTPVersion httpVersion; - private HTTP2 http2; + private Http2 http2; public EncodeHTTPBase() { super("http/1.1"); @@ -42,7 +42,7 @@ public EncodeHTTPBase(String ALPN) throws Exception { httpVersion = HTTPVersion.HTTP1; } else if (ALPN.equals("h2") || ALPN.equals("grpc")) { httpVersion = HTTPVersion.HTTP2; - http2 = new HTTP2(); + http2 = new Http2(); } else { httpVersion = HTTPVersion.HTTP1; } diff --git a/src/main/java/core/packetproxy/encode/EncodeHTTPStreamingResponse.java b/src/main/java/core/packetproxy/encode/EncodeHTTPStreamingResponse.java index 3a422d8..f21e454 100644 --- a/src/main/java/core/packetproxy/encode/EncodeHTTPStreamingResponse.java +++ b/src/main/java/core/packetproxy/encode/EncodeHTTPStreamingResponse.java @@ -18,8 +18,8 @@ import java.io.InputStream; import packetproxy.http.Http; -import packetproxy.http2.HTTP2; -import packetproxy.http2.StreamingResponse; +import packetproxy.http1.Http1StreamingResponse; +import packetproxy.http2.Http2StreamingResponse; import packetproxy.model.Packet; public class EncodeHTTPStreamingResponse extends Encoder @@ -28,7 +28,8 @@ public enum HTTPVersion { HTTP1, HTTP2 } private HTTPVersion httpVersion; - private StreamingResponse http2StreamingResponse; + private Http1StreamingResponse http1StreamingResponse; + private Http2StreamingResponse http2StreamingResponse; public EncodeHTTPStreamingResponse() { super("http/1.1"); @@ -39,12 +40,15 @@ public EncodeHTTPStreamingResponse(String ALPN) throws Exception { super(ALPN); if (ALPN == null) { httpVersion = HTTPVersion.HTTP1; + } else if (ALPN.equals("http/1.0") || ALPN.equals("http/1.1")) { + httpVersion = HTTPVersion.HTTP1; } else if (ALPN.equals("h2") || ALPN.equals("grpc")) { httpVersion = HTTPVersion.HTTP2; - http2StreamingResponse = new StreamingResponse(); } else { httpVersion = HTTPVersion.HTTP1; } + http1StreamingResponse = new Http1StreamingResponse(); + http2StreamingResponse = new Http2StreamingResponse(); } public HTTPVersion getHttpVersion() { return httpVersion; } @@ -53,47 +57,56 @@ public EncodeHTTPStreamingResponse(String ALPN) throws Exception { public String getName() { return "HTTP Streaming Response"; } + + @Override + public int checkRequestDelimiter(byte[] data) throws Exception { + if (this.httpVersion == HTTPVersion.HTTP1) { + return http1StreamingResponse.checkRequestDelimiter(data); + } else { + return http2StreamingResponse.checkDelimiter(data); + } + } @Override - public int checkDelimiter(byte[] data) throws Exception { + public int checkResponseDelimiter(byte[] data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return Http.parseHttpDelimiter(data); + return http1StreamingResponse.checkResponseDelimiter(data); } else { return http2StreamingResponse.checkDelimiter(data); } } @Override - public void clientRequestArrived(byte[] frames) throws Exception { + public void clientRequestArrived(byte[] data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - super.clientRequestArrived(frames); + http1StreamingResponse.clientRequestArrived(data); } else { - http2StreamingResponse.clientRequestArrived(frames); + http2StreamingResponse.clientRequestArrived(data); } } @Override - public void serverResponseArrived(byte[] frames) throws Exception { + public void serverResponseArrived(byte[] data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - super.serverResponseArrived(frames); + http1StreamingResponse.serverResponseArrived(data); } else { - http2StreamingResponse.serverResponseArrived(frames); + http2StreamingResponse.serverResponseArrived(data); } } @Override public byte[] passThroughClientRequest() throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return super.passThroughClientRequest(); + return http1StreamingResponse.passThroughClientRequest(); } else { return http2StreamingResponse.passThroughClientRequest(); } } - + @Override public byte[] passThroughServerResponse() throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return super.passThroughServerResponse(); + return http1StreamingResponse.passThroughServerResponse(); } else { return http2StreamingResponse.passThroughServerResponse(); } @@ -102,7 +115,7 @@ public byte[] passThroughServerResponse() throws Exception { @Override public byte[] clientRequestAvailable() throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return super.clientRequestAvailable(); + return http1StreamingResponse.clientRequestAvailable(); } else { return http2StreamingResponse.clientRequestAvailable(); } @@ -111,7 +124,7 @@ public byte[] clientRequestAvailable() throws Exception { @Override public byte[] serverResponseAvailable() throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return super.serverResponseAvailable(); + return http1StreamingResponse.serverResponseAvailable(); } else { return http2StreamingResponse.serverResponseAvailable(); } @@ -120,7 +133,7 @@ public byte[] serverResponseAvailable() throws Exception { @Override final public byte[] decodeServerResponse(byte[] input_data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return input_data; + return http1StreamingResponse.decodeServerResponse(input_data); } else { return http2StreamingResponse.decodeServerResponse(input_data); } @@ -129,7 +142,7 @@ final public byte[] decodeServerResponse(byte[] input_data) throws Exception { @Override public byte[] encodeServerResponse(byte[] input_data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return input_data; + return http1StreamingResponse.encodeServerResponse(input_data); } else { return http2StreamingResponse.encodeServerResponse(input_data); } @@ -138,7 +151,7 @@ public byte[] encodeServerResponse(byte[] input_data) throws Exception { @Override public byte[] decodeClientRequest(byte[] input_data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return input_data; + return http1StreamingResponse.decodeClientRequest(input_data); } else { return http2StreamingResponse.decodeClientRequest(input_data); } @@ -147,7 +160,7 @@ public byte[] decodeClientRequest(byte[] input_data) throws Exception { @Override public byte[] encodeClientRequest(byte[] input_data) throws Exception { if (this.httpVersion == HTTPVersion.HTTP1) { - return input_data; + return http1StreamingResponse.encodeClientRequest(input_data); } else { return http2StreamingResponse.encodeClientRequest(input_data); } @@ -196,8 +209,7 @@ public String getContentType(byte[] input_data) throws Exception { } @Override - public String getSummarizedResponse(Packet packet) - { + public String getSummarizedResponse(Packet packet) { String summary = ""; if (packet.getDecodedData().length == 0 && packet.getModifiedData().length == 0) { return ""; } try { @@ -213,8 +225,7 @@ public String getSummarizedResponse(Packet packet) } @Override - public String getSummarizedRequest(Packet packet) - { + public String getSummarizedRequest(Packet packet) { String summary = ""; if (packet.getDecodedData().length == 0 && packet.getModifiedData().length == 0) { return ""; } try { @@ -237,4 +248,9 @@ public void setGroupId(Packet packet) throws Exception { } } + @Override + public int checkDelimiter(byte[] input_data) throws Exception { + return input_data.length; + } + } diff --git a/src/main/java/core/packetproxy/gui/GUIOption.java b/src/main/java/core/packetproxy/gui/GUIOption.java index 593a580..2ecd23f 100644 --- a/src/main/java/core/packetproxy/gui/GUIOption.java +++ b/src/main/java/core/packetproxy/gui/GUIOption.java @@ -150,6 +150,12 @@ public void actionPerformed(ActionEvent ae) { panel.add(createSeparator()); + panel.add(createElement("Priority Order of HTTP Versions", I18nString.get("Set order of priority between HTTP1 and HTTP2."))); + GUIOptionHttp http = new GUIOptionHttp(); + panel.add(http.createPanel()); + + panel.add(createSeparator()); + panel.add(createElement("PacketProxy CA Certificates", I18nString.get("Export CA certificate used to view SSL packets. It needs to be registered in trusted CA list of PC/Mac/Linux/Android/iOS"))); JPanel caPanel = new JPanel(); diff --git a/src/main/java/core/packetproxy/gui/GUIOptionHttp.java b/src/main/java/core/packetproxy/gui/GUIOptionHttp.java new file mode 100644 index 0000000..fd37ae7 --- /dev/null +++ b/src/main/java/core/packetproxy/gui/GUIOptionHttp.java @@ -0,0 +1,61 @@ +package packetproxy.gui; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import javax.swing.BoxLayout; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import packetproxy.model.ConfigString; + +public class GUIOptionHttp { + + private JComboBox combo = new JComboBox<>(); + private ConfigString configPriority = new ConfigString("PriorityOrderOfHttpVersions"); + + public GUIOptionHttp() throws Exception { + combo.setPrototypeDisplayValue("xxxxxxx"); + combo.addItem("HTTP1"); + combo.addItem("HTTP2"); + combo.setMaximumRowCount(combo.getItemCount()); + String priority = configPriority.getString(); + if (priority == null || priority.length() == 0) { + configPriority.setString("HTTP2"); + priority = configPriority.getString(); + } + combo.setSelectedItem(priority); + combo.addItemListener(new ItemListener(){ + @Override + public void itemStateChanged(ItemEvent event) { + try { + if (event.getStateChange() != ItemEvent.SELECTED || combo.getSelectedItem() == null) { + return; + } + String priority = (String)combo.getSelectedItem(); + configPriority.setString(priority); + combo.setSelectedItem(priority); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + combo.setMaximumSize(new Dimension(combo.getPreferredSize().width, combo.getMinimumSize().height)); + } + + public JPanel createPanel() throws Exception { + JPanel panel = new JPanel(); + panel.setBackground(Color.WHITE); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + panel.add(combo); + panel.add(new JLabel("has a high priority")); + panel.setAlignmentX(Component.LEFT_ALIGNMENT); + panel.setMaximumSize(new Dimension(Short.MAX_VALUE, panel.getMaximumSize().height)); + return panel; + } + +} diff --git a/src/main/java/core/packetproxy/http/Http.java b/src/main/java/core/packetproxy/http/Http.java index 7176ec1..21d6175 100644 --- a/src/main/java/core/packetproxy/http/Http.java +++ b/src/main/java/core/packetproxy/http/Http.java @@ -261,7 +261,7 @@ private byte[] getCookedBody(HttpHeader header, byte[] rawBody) throws Exception if (enc.isPresent() && enc.get().equalsIgnoreCase("chunked")) { header.removeAll(headerName); - cookedBody = getChankedHttpBody(cookedBody); + cookedBody = getChankedHttpBodyFussy(cookedBody); if (cookedBody == null) return null; } @@ -495,6 +495,31 @@ private static byte[] gzip(byte[] input_data) throws Exception return out.toByteArray(); } + private static byte[] getChankedHttpBodyFussy(byte[] input_data) throws Exception + { + // TODO 改行コードの対応 + byte[] search_word = new String("\r\n").getBytes(); + int index = 0; + int start_index = 0; + byte[] body = new byte[0]; + while ((index = Utils.indexOf(input_data, start_index, input_data.length, search_word)) >= 0) { + try { + byte[] chank_header = ArrayUtils.subarray(input_data, start_index, index); + String chank_length_str = new String(chank_header, "UTF-8").replaceAll("^0+([^0].*)$", "$1"); + int chank_length = Integer.parseInt(chank_length_str.trim(), 16); + if (chank_length == 0) { + return body; + } + byte[] chank = ArrayUtils.subarray(input_data, index + search_word.length, index + search_word.length + chank_length); + body = ArrayUtils.addAll(body, chank); + start_index = index + search_word.length*2 + chank_length; + } catch (Exception e) { + return body; + } + } + return body; + } + private static byte[] getChankedHttpBody(byte[] input_data) throws Exception { // TODO 改行コードの対応 @@ -514,9 +539,7 @@ private static byte[] getChankedHttpBody(byte[] input_data) throws Exception body = ArrayUtils.addAll(body, chank); start_index = index + search_word.length*2 + chank_length; } catch (Exception e) { - e.printStackTrace(); - Binary b = new Binary(input_data); - PacketProxyUtility.getInstance().packetProxyLog(new String(b.toHexString().toString())); + return null; } } return null; diff --git a/src/main/java/core/packetproxy/http/Https.java b/src/main/java/core/packetproxy/http/Https.java index 8b1c375..f6de2f0 100644 --- a/src/main/java/core/packetproxy/http/Https.java +++ b/src/main/java/core/packetproxy/http/Https.java @@ -26,6 +26,7 @@ import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import javax.net.ServerSocketFactory; @@ -49,6 +50,7 @@ import packetproxy.CertCacheManager; import packetproxy.common.ClientKeyManager; import packetproxy.common.Utils; +import packetproxy.model.ConfigString; import packetproxy.model.Server; import packetproxy.model.Servers; import packetproxy.model.CAs.CA; @@ -106,7 +108,16 @@ public static SSLSocket[] createBothSideSSLSockets(Socket clientSocket, InputStr serverSSLSocket[0] = (SSLSocket) createSSLSocketFactory().createSocket(serverSocket, null, true); serverSSLSocket[0].setUseClientMode(true); SSLParameters sp = serverSSLSocket[0].getSSLParameters(); - sp.setApplicationProtocols(clientProtocols.toArray(new String[clientProtocols.size()])); + + List alpns = new LinkedList<>(clientProtocols); + if (new ConfigString("PriorityOrderOfHttpVersions").getString().equals("HTTP1")) { + if (alpns.contains("http/1.1") || alpns.contains("http/1.0")) { + alpns.remove("h2"); + alpns.remove("grpc"); + } + } + sp.setApplicationProtocols(alpns.toArray(new String[alpns.size()])); + serverSSLSocket[0].setSSLParameters(sp); serverSSLSocket[0].startHandshake(); } catch (Exception e) { diff --git a/src/main/java/core/packetproxy/http1/Http1StreamingResponse.java b/src/main/java/core/packetproxy/http1/Http1StreamingResponse.java new file mode 100644 index 0000000..b9ddbe9 --- /dev/null +++ b/src/main/java/core/packetproxy/http1/Http1StreamingResponse.java @@ -0,0 +1,93 @@ +package packetproxy.http1; + +import java.io.ByteArrayOutputStream; +import java.util.List; + +import org.apache.commons.lang3.ArrayUtils; + +import packetproxy.common.StringUtils; +import packetproxy.http.Http; +import packetproxy.model.Packet; +import packetproxy.model.Packets; + +public class Http1StreamingResponse +{ + private ByteArrayOutputStream clientInput = new ByteArrayOutputStream(); + private ByteArrayOutputStream serverInput = new ByteArrayOutputStream(); + + private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + private ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(); + private boolean headerReceived = false; + + public int checkRequestDelimiter(byte[] data) throws Exception { return Http.parseHttpDelimiter(data); } + public void clientRequestArrived(byte[] data) throws Exception { clientInput.write(data); } + public byte[] passThroughClientRequest() throws Exception { return null; }; + public byte[] clientRequestAvailable() throws Exception { byte[] ret = clientInput.toByteArray(); clientInput.reset(); return ret; } + public byte[] decodeClientRequest(byte[] input_data) throws Exception { return input_data; } + public byte[] encodeClientRequest(byte[] input_data) throws Exception { return input_data; } + + public int checkResponseDelimiter(byte[] data) throws Exception { return data.length; } + public void serverResponseArrived(byte[] data) throws Exception { serverInput.write(data); } + public byte[] passThroughServerResponse() throws Exception { + buffer.write(serverInput.toByteArray()); + if (headerReceived == false) { + int endOfHeader = StringUtils.binaryFind(buffer.toByteArray(), "\r\n\r\n".getBytes()); + if (endOfHeader > 0) { + byte[] header = ArrayUtils.subarray(buffer.toByteArray(), 0, endOfHeader + 2); + byte[] body = ArrayUtils.subarray(buffer.toByteArray(), endOfHeader + 4, buffer.size()); + byte[] newHeader = ArrayUtils.addAll(header, String.format("X-PacketProxy-HTTP1-UUID: %s\r\n\r\n", StringUtils.randomUUID()).getBytes()); + byte[] newHttp = ArrayUtils.addAll(newHeader, body); + buffer.reset(); + buffer.write(newHttp); + headerBuffer.write(newHeader); + headerReceived = true; + } + } else { + Http http; + int delim = Http.parseHttpDelimiter(buffer.toByteArray()); + if (delim > 0) { + byte[] httpData = ArrayUtils.subarray(buffer.toByteArray(), 0, delim); + byte[] remaining = ArrayUtils.subarray(buffer.toByteArray(), delim, buffer.size()); + headerReceived = false; + buffer.reset(); + buffer.write(remaining); + http = new Http(httpData); + } else { + http = new Http(buffer.toByteArray()); + } + Thread guiHistoryUpdater = new Thread(new Runnable() { + public void run() { + try { + if (http.getBody() != null && http.getBody().length > 0) { + List packets = Packets.getInstance().queryFullText(http.getFirstHeader("X-PacketProxy-HTTP1-UUID")); + for (Packet packet: packets) { + Packet p = Packets.getInstance().query(packet.getId()); + p.setDecodedData(http.toByteArray()); + p.setModifiedData(http.toByteArray()); + Packets.getInstance().update(p); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + guiHistoryUpdater.start(); + } + byte[] out = serverInput.toByteArray(); + serverInput.reset(); + return out; + } + public byte[] serverResponseAvailable() throws Exception { + if (headerBuffer.size() == 0) { + return null; + } + byte[] out = headerBuffer.toByteArray(); + headerBuffer.reset(); + return out; + } + public byte[] decodeServerResponse(byte[] input_data) throws Exception { return input_data; } + public byte[] encodeServerResponse(byte[] input_data) throws Exception { return null; } + + +} diff --git a/src/main/java/core/packetproxy/http2/HTTP2.java b/src/main/java/core/packetproxy/http2/HTTP2.java index 770e65f..38ee421 100644 --- a/src/main/java/core/packetproxy/http2/HTTP2.java +++ b/src/main/java/core/packetproxy/http2/HTTP2.java @@ -30,12 +30,12 @@ import packetproxy.http2.frames.HeadersFrame; import packetproxy.model.Packet; -public class HTTP2 extends FramesBase +public class Http2 extends FramesBase { private StreamManager clientStreamManager = new StreamManager(); private StreamManager serverStreamManager = new StreamManager(); - public HTTP2() throws Exception { + public Http2() throws Exception { super(); } @@ -45,14 +45,9 @@ public String getName() { } @Override - protected byte[] passFramesToDecodeClientRequest(List frames) throws Exception { - return filterFrames(clientStreamManager, frames); - } - + protected byte[] passFramesToDecodeClientRequest(List frames) throws Exception { return filterFrames(clientStreamManager, frames); } @Override - protected byte[] passFramesToDecodeServerResponse(List frames) throws Exception { - return filterFrames(serverStreamManager, frames); - } + protected byte[] passFramesToDecodeServerResponse(List frames) throws Exception { return filterFrames(serverStreamManager, frames); } private byte[] filterFrames(StreamManager streamManager, List frames) throws Exception { for (Frame frame : frames) { diff --git a/src/main/java/core/packetproxy/http2/Http2.java b/src/main/java/core/packetproxy/http2/Http2.java new file mode 100644 index 0000000..38ee421 --- /dev/null +++ b/src/main/java/core/packetproxy/http2/Http2.java @@ -0,0 +1,132 @@ +/* + * Copyright 2019 DeNA Co., Ltd. + * + * 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 packetproxy.http2; + +import java.io.ByteArrayOutputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.http2.hpack.HpackEncoder; + +import packetproxy.common.UniqueID; +import packetproxy.http.Http; +import packetproxy.http2.frames.DataFrame; +import packetproxy.http2.frames.Frame; +import packetproxy.http2.frames.FrameUtils; +import packetproxy.http2.frames.HeadersFrame; +import packetproxy.model.Packet; + +public class Http2 extends FramesBase +{ + private StreamManager clientStreamManager = new StreamManager(); + private StreamManager serverStreamManager = new StreamManager(); + + public Http2() throws Exception { + super(); + } + + @Override + public String getName() { + return "HTTP2"; + } + + @Override + protected byte[] passFramesToDecodeClientRequest(List frames) throws Exception { return filterFrames(clientStreamManager, frames); } + @Override + protected byte[] passFramesToDecodeServerResponse(List frames) throws Exception { return filterFrames(serverStreamManager, frames); } + + private byte[] filterFrames(StreamManager streamManager, List frames) throws Exception { + for (Frame frame : frames) { + if (frame instanceof HeadersFrame) { + streamManager.write(frame); + } else if (frame instanceof DataFrame) { + streamManager.write(frame); + } + if ((frame.getFlags() & 0x01) > 0) { + List stream = streamManager.read(frame.getStreamId()); + return FrameUtils.toByteArray(stream); + } + } + return null; + } + + @Override + protected byte[] decodeClientRequestFromFrames(byte[] frames) throws Exception { return decodeFromFrames(frames); } + @Override + protected byte[] decodeServerResponseFromFrames(byte[] frames) throws Exception { return decodeFromFrames(frames); } + + private byte[] decodeFromFrames(byte[] frames) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (Frame frame : FrameUtils.parseFrames(frames)) { + if (frame instanceof HeadersFrame) { + HeadersFrame headersFrame = (HeadersFrame)frame; + out.write(headersFrame.getHttp()); + } else if (frame instanceof DataFrame) { + DataFrame dataFrame = (DataFrame)frame; + out.write(dataFrame.getPayload()); + } + } + Http http = new Http(out.toByteArray()); + int flags = Integer.valueOf(http.getFirstHeader("X-PacketProxy-HTTP2-Flags")); + if (http.getBody() == null || http.getBody().length == 0) { + http.updateHeader("X-PacketProxy-HTTP2-Flags", String.valueOf(flags & 0xff | HeadersFrame.FLAG_END_STREAM)); + } + return http.toByteArray(); + } + + @Override + protected byte[] encodeClientRequestToFrames(byte[] http) throws Exception { return encodeToFrames(http, super.getClientHpackEncoder()); } + @Override + protected byte[] encodeServerResponseToFrames(byte[] http) throws Exception { return encodeToFrames(http, super.getServerHpackEncoder()); } + + private byte[] encodeToFrames(byte[] data, HpackEncoder encoder) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Http http = new Http(data); + int flags = Integer.valueOf(http.getFirstHeader("X-PacketProxy-HTTP2-Flags")); + if (http.getBody() != null && http.getBody().length > 0) { + http.updateHeader("X-PacketProxy-HTTP2-Flags", String.valueOf(flags & 0xff & ~HeadersFrame.FLAG_END_STREAM)); + HeadersFrame headersFrame = new HeadersFrame(http); + out.write(headersFrame.toByteArrayWithoutExtra(encoder)); + DataFrame dataFrame = new DataFrame(http); + out.write(dataFrame.toByteArrayWithoutExtra()); + } else { + http.updateHeader("X-PacketProxy-HTTP2-Flags", String.valueOf(flags & 0xff | HeadersFrame.FLAG_END_STREAM)); + HeadersFrame headersFrame = new HeadersFrame(http); + out.write(headersFrame.toByteArrayWithoutExtra(encoder)); + } + return out.toByteArray(); + } + + /* key: streamId, value: groupId */ + private Map groupMap = new HashMap<>(); + + public void setGroupId(Packet packet) throws Exception { + byte[] data = (packet.getDecodedData().length > 0) ? packet.getDecodedData() : packet.getModifiedData(); + Http http = new Http(data); + String streamIdStr = http.getFirstHeader("X-PacketProxy-HTTP2-Stream-Id"); + if (streamIdStr != null && streamIdStr.length() > 0) { + long streamId = Long.parseLong(streamIdStr); + if (groupMap.containsKey(streamId)) { + packet.setGroup(groupMap.get(streamId)); + } else { + long groupId = UniqueID.getInstance().createId(); + groupMap.put(streamId, groupId); + packet.setGroup(groupId); + } + } + } +} diff --git a/src/main/java/core/packetproxy/http2/StreamingResponse.java b/src/main/java/core/packetproxy/http2/Http2StreamingResponse.java similarity index 98% rename from src/main/java/core/packetproxy/http2/StreamingResponse.java rename to src/main/java/core/packetproxy/http2/Http2StreamingResponse.java index c83471f..6ae4f7e 100644 --- a/src/main/java/core/packetproxy/http2/StreamingResponse.java +++ b/src/main/java/core/packetproxy/http2/Http2StreamingResponse.java @@ -33,12 +33,12 @@ import packetproxy.model.Packet; import packetproxy.model.Packets; -public class StreamingResponse extends FramesBase +public class Http2StreamingResponse extends FramesBase { private StreamManager clientStreamManager = new StreamManager(); private StreamManager serverStreamManager = new StreamManager(); - public StreamingResponse() throws Exception { + public Http2StreamingResponse() throws Exception { } StreamManager stream = new StreamManager(); @@ -136,7 +136,6 @@ private byte[] filterFrames(StreamManager streamManager, List frames) thr @Override protected byte[] decodeClientRequestFromFrames(byte[] frames) throws Exception { return decodeFromFrames(frames); } - @Override protected byte[] decodeServerResponseFromFrames(byte[] frames) throws Exception { return decodeFromFrames(frames); } @@ -161,7 +160,6 @@ private byte[] decodeFromFrames(byte[] frames) throws Exception { @Override protected byte[] encodeClientRequestToFrames(byte[] http) throws Exception { return encodeToFrames(http, super.getClientHpackEncoder()); } - @Override protected byte[] encodeServerResponseToFrames(byte[] http) throws Exception { return null; } From 2de38c96bcfdc936ad021451a0f89938581fd0ad Mon Sep 17 00:00:00 2001 From: funa-tk <1781263+funa-tk@users.noreply.github.com> Date: Mon, 16 Dec 2019 01:42:32 +0900 Subject: [PATCH 2/2] delete HTTP2.java --- .../java/core/packetproxy/http2/HTTP2.java | 132 ------------------ 1 file changed, 132 deletions(-) delete mode 100644 src/main/java/core/packetproxy/http2/HTTP2.java diff --git a/src/main/java/core/packetproxy/http2/HTTP2.java b/src/main/java/core/packetproxy/http2/HTTP2.java deleted file mode 100644 index 38ee421..0000000 --- a/src/main/java/core/packetproxy/http2/HTTP2.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2019 DeNA Co., Ltd. - * - * 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 packetproxy.http2; - -import java.io.ByteArrayOutputStream; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.jetty.http2.hpack.HpackEncoder; - -import packetproxy.common.UniqueID; -import packetproxy.http.Http; -import packetproxy.http2.frames.DataFrame; -import packetproxy.http2.frames.Frame; -import packetproxy.http2.frames.FrameUtils; -import packetproxy.http2.frames.HeadersFrame; -import packetproxy.model.Packet; - -public class Http2 extends FramesBase -{ - private StreamManager clientStreamManager = new StreamManager(); - private StreamManager serverStreamManager = new StreamManager(); - - public Http2() throws Exception { - super(); - } - - @Override - public String getName() { - return "HTTP2"; - } - - @Override - protected byte[] passFramesToDecodeClientRequest(List frames) throws Exception { return filterFrames(clientStreamManager, frames); } - @Override - protected byte[] passFramesToDecodeServerResponse(List frames) throws Exception { return filterFrames(serverStreamManager, frames); } - - private byte[] filterFrames(StreamManager streamManager, List frames) throws Exception { - for (Frame frame : frames) { - if (frame instanceof HeadersFrame) { - streamManager.write(frame); - } else if (frame instanceof DataFrame) { - streamManager.write(frame); - } - if ((frame.getFlags() & 0x01) > 0) { - List stream = streamManager.read(frame.getStreamId()); - return FrameUtils.toByteArray(stream); - } - } - return null; - } - - @Override - protected byte[] decodeClientRequestFromFrames(byte[] frames) throws Exception { return decodeFromFrames(frames); } - @Override - protected byte[] decodeServerResponseFromFrames(byte[] frames) throws Exception { return decodeFromFrames(frames); } - - private byte[] decodeFromFrames(byte[] frames) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - for (Frame frame : FrameUtils.parseFrames(frames)) { - if (frame instanceof HeadersFrame) { - HeadersFrame headersFrame = (HeadersFrame)frame; - out.write(headersFrame.getHttp()); - } else if (frame instanceof DataFrame) { - DataFrame dataFrame = (DataFrame)frame; - out.write(dataFrame.getPayload()); - } - } - Http http = new Http(out.toByteArray()); - int flags = Integer.valueOf(http.getFirstHeader("X-PacketProxy-HTTP2-Flags")); - if (http.getBody() == null || http.getBody().length == 0) { - http.updateHeader("X-PacketProxy-HTTP2-Flags", String.valueOf(flags & 0xff | HeadersFrame.FLAG_END_STREAM)); - } - return http.toByteArray(); - } - - @Override - protected byte[] encodeClientRequestToFrames(byte[] http) throws Exception { return encodeToFrames(http, super.getClientHpackEncoder()); } - @Override - protected byte[] encodeServerResponseToFrames(byte[] http) throws Exception { return encodeToFrames(http, super.getServerHpackEncoder()); } - - private byte[] encodeToFrames(byte[] data, HpackEncoder encoder) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - Http http = new Http(data); - int flags = Integer.valueOf(http.getFirstHeader("X-PacketProxy-HTTP2-Flags")); - if (http.getBody() != null && http.getBody().length > 0) { - http.updateHeader("X-PacketProxy-HTTP2-Flags", String.valueOf(flags & 0xff & ~HeadersFrame.FLAG_END_STREAM)); - HeadersFrame headersFrame = new HeadersFrame(http); - out.write(headersFrame.toByteArrayWithoutExtra(encoder)); - DataFrame dataFrame = new DataFrame(http); - out.write(dataFrame.toByteArrayWithoutExtra()); - } else { - http.updateHeader("X-PacketProxy-HTTP2-Flags", String.valueOf(flags & 0xff | HeadersFrame.FLAG_END_STREAM)); - HeadersFrame headersFrame = new HeadersFrame(http); - out.write(headersFrame.toByteArrayWithoutExtra(encoder)); - } - return out.toByteArray(); - } - - /* key: streamId, value: groupId */ - private Map groupMap = new HashMap<>(); - - public void setGroupId(Packet packet) throws Exception { - byte[] data = (packet.getDecodedData().length > 0) ? packet.getDecodedData() : packet.getModifiedData(); - Http http = new Http(data); - String streamIdStr = http.getFirstHeader("X-PacketProxy-HTTP2-Stream-Id"); - if (streamIdStr != null && streamIdStr.length() > 0) { - long streamId = Long.parseLong(streamIdStr); - if (groupMap.containsKey(streamId)) { - packet.setGroup(groupMap.get(streamId)); - } else { - long groupId = UniqueID.getInstance().createId(); - groupMap.put(streamId, groupId); - packet.setGroup(groupId); - } - } - } -}