Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9d96605
リクエストとレスポンスを同じ行に表示
Dec 24, 2025
8a80015
RequestとResponse内にタブを移動
Dec 24, 2025
2851dd0
Request/Responseの並列表示の実装で循環依存があったので修正
Dec 25, 2025
2ff9e22
groudIDで行のマージを判断するように変更。
Dec 25, 2025
c12cea6
format
taka2233 Jan 20, 2026
1959bda
add: 同一groupIDのパケットが2から3個になった際に、Streamingとして扱う処理を追加
taka2233 Jan 20, 2026
680d5e4
fix: コンフリクトマーカーが残っていたので削除
taka2233 Jan 20, 2026
aa2fc6d
refactor: Kotlinに置き換え
taka2233 Jan 20, 2026
9ae467d
format
taka2233 Jan 20, 2026
14db1a9
ch: Response受信時の挙動をStreamingからRequest/Responseの分割表示に変更
taka2233 Feb 4, 2026
797c30b
fix: grpc streamingなどで、同一Group IDでRequestが先に2つ送信される場合に、Streaming扱いになら…
taka2233 Feb 5, 2026
c0d98a2
fix: タブの順番が変わっていたので修正
taka2233 Feb 5, 2026
6480a52
fix: グループのマージ条件を更新し、CLIENTパケット数の制限を追加
taka2233 Feb 20, 2026
9128335
fix: 既存のマージを解除する条件を更新し、非同期モデルでの処理を追加
taka2233 Feb 20, 2026
59939d1
fix: 既存のパケット削除処理を更新し、リクエストに関連するレスポンスパケットも同時に削除するように修正
taka2233 Feb 20, 2026
450666b
add: 新しいテストクラスPacketPairingServiceTestを追加し、PacketPairingServiceの機能を検証…
taka2233 Feb 20, 2026
e3b6439
refactor: Update GUIHistory and GUIPacket to improve packet handling …
taka2233 Feb 20, 2026
04e8202
chore: Copyrightを修正
taka2233 Feb 20, 2026
b63db5c
add: テストケースを追加
taka2233 Feb 20, 2026
92b500d
fix: 下部のボタンパネルを分割表示の対象外に変更
taka2233 Feb 20, 2026
6738902
fix: Remove unused import of FlowLayout in GUIData.java
taka2233 Feb 20, 2026
c1dbd61
feat: Diffマーク時にマージ行のRequest/Response選択ダイアログを追加
taka2233 Feb 24, 2026
96fa7b4
refactor: コメントの整理
taka2233 Feb 24, 2026
41f2254
fix: errorを返す用に修正
taka2233 Feb 24, 2026
49eebac
fix: Resenderなどで再送した際に、HistoryタブでRequest内容が空になる問題を修正
taka2233 Feb 24, 2026
91d6f5a
fix: Interceptorでレスポンス改ざん時にModified列が更新されない問題を修正
taka2233 Feb 24, 2026
41fdf2d
refactor: Clean up imports and improve comments in GUIData.java
taka2233 Feb 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions src/main/java/core/packetproxy/DuplexFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,14 @@ public int onServerPacketReceived(byte[] data) throws Exception {

@Override
public byte[] onClientChunkReceived(byte[] data) throws Exception {
long initialGroupId = UniqueID.getInstance().createId();
client_packet = new Packet(0, client_addr, server_addr, server_endpoint.getName(), use_ssl,
encoder_name, ALPN, Packet.Direction.CLIENT, duplex.hashCode(),
UniqueID.getInstance().createId());
packets.update(client_packet);
encoder_name, ALPN, Packet.Direction.CLIENT, duplex.hashCode(), initialGroupId);
client_packet.setReceivedData(data);
if (data.length < SKIP_LENGTH) {

packets.update(client_packet);
}
byte[] decoded_data = encoder.decodeClientRequest(client_packet);
client_packet.setDecodedData(decoded_data);
// groupIdはencoder.setGroupId()で変更される可能性があるため、
// GUIHistoryへの通知(packets.update)はgroupId確定後に行う
encoder.setGroupId(client_packet); /* 実行するのはsetDecodedDataのあと */
if (data.length < SKIP_LENGTH) {

Expand Down Expand Up @@ -408,6 +405,7 @@ public byte[] onClientChunkSend(byte[] data) throws Exception {
oneshot.getUseSSL(), oneshot.getEncoder(), oneshot.getAlpn(), Packet.Direction.CLIENT,
duplex.hashCode(), UniqueID.getInstance().createId());
client_packet.setModified();
client_packet.setReceivedData(data);
client_packet.setDecodedData(data);
client_packet.setModifiedData(data);
if (data.length < SKIP_LENGTH) {
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/core/packetproxy/encode/EncodeHTTPBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,21 @@ public String getContentType(byte[] input_data) throws Exception {
return http.getFirstHeader("Content-Type");
}

/**
* レスポンスからContent-Typeを取得し、存在しない場合はリクエストのContent-Typeをフォールバックとして使用する。
* gRPC等のプロトコルでは、レスポンスにContent-Typeが含まれないことがあるため、
* リクエストのContent-Typeを使用することで、History一覧のType列に適切な値を表示する。
*/
@Override
public String getContentType(Packet client_packet, Packet server_packet) throws Exception {
String contentType = getContentType(server_packet.getDecodedData());
if (contentType.isEmpty() && client_packet != null && client_packet.getDecodedData().length > 0) {

contentType = getContentType(client_packet.getDecodedData());
}
return contentType;
}

@Override
public String getSummarizedResponse(Packet packet) {
String summary = "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public void mouseExited(MouseEvent e) {
button.setIcon(icon);
}
});
main_panel.add(javax.swing.Box.createHorizontalStrut(8));
main_panel.add(label);
// 数字とバツボタンの間に余白を追加
main_panel.add(Box.createHorizontalStrut(7));
Expand Down
162 changes: 124 additions & 38 deletions src/main/java/core/packetproxy/gui/GUIData.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
Expand All @@ -35,11 +37,12 @@
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.Scrollable;
import javax.swing.border.LineBorder;
import packetproxy.controller.ResendController;
import packetproxy.controller.SinglePacketAttackController;
Expand Down Expand Up @@ -78,13 +81,86 @@ public GUIData(JFrame owner) {
}

public JComponent createPanel() throws Exception {
createTabsPanel();
main_panel.add(createButtonPanel());
return main_panel;
}

public JComponent createTabsPanel() throws Exception {
main_panel = new JPanel();
main_panel.setLayout(new BoxLayout(main_panel, BoxLayout.Y_AXIS));

tabs = new TabSet(true, false);

main_panel.add(tabs.getTabPanel());

initButtons();
return main_panel;
}

public JComponent createButtonPanel() {
JPanel diff_panel = new JPanel();
diff_panel.add(diff_orig_button);
diff_panel.add(diff_button);
diff_panel.add(stop_diff_button);
diff_panel.setBorder(new LineBorder(Color.black, 1, true));
diff_panel.setLayout(new BoxLayout(diff_panel, BoxLayout.LINE_AXIS));

JPanel button_panel = new JPanel();
button_panel.add(charSetCombo);
button_panel.add(copy_url_body_button);
button_panel.add(copy_body_button);
button_panel.add(copy_url_button);
button_panel.add(resend_button);
button_panel.add(resend_multiple_button);
button_panel.add(attack_button);
button_panel.add(send_to_resender_button);
button_panel.add(new JLabel(" diff: "));
button_panel.add(diff_panel);
button_panel.setLayout(new BoxLayout(button_panel, BoxLayout.LINE_AXIS));

ScrollableCenteredPanel centered_panel = new ScrollableCenteredPanel();
centered_panel.add(button_panel);
return createButtonScrollPane(centered_panel);
}

/**
* ビューポートが十分に広い場合はボタンを中央寄せし、 狭い場合は横スクロールバーを表示するためのパネル。
* getScrollableTracksViewportWidth() でビューポート幅に追従するかを切り替える。
*/
private static class ScrollableCenteredPanel extends JPanel implements Scrollable {

ScrollableCenteredPanel() {
super(new FlowLayout(FlowLayout.CENTER));
}

@Override
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}

@Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 20;
}

@Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 100;
}

@Override
public boolean getScrollableTracksViewportWidth() {
return getParent() != null && getParent().getWidth() >= getPreferredSize().width;
}

@Override
public boolean getScrollableTracksViewportHeight() {
return true;
}
}

private void initButtons() throws Exception {
copy_url_body_button = new JButton("copy Method+URL+Body");
copy_url_body_button.addActionListener(new ActionListener() {

Expand Down Expand Up @@ -313,9 +389,12 @@ public void actionPerformed(ActionEvent e) {
public void actionPerformed(ActionEvent e) {
try {

Diff.getInstance().markAsTarget(tabs.getRaw().getData());
DiffBinary.getInstance().markAsTarget(tabs.getBinary().getData());
DiffJson.getInstance().markAsTarget(tabs.getJson().getData());
byte[] data = resolveDataForDiff();
if (data == null)
return;
Diff.getInstance().markAsTarget(data);
DiffBinary.getInstance().markAsTarget(data);
DiffJson.getInstance().markAsTarget(data);
GUIDiffDialogParent dlg = new GUIDiffDialogParent(owner);
dlg.showDialog();
} catch (Exception e1) {
Expand All @@ -333,6 +412,9 @@ public void actionPerformed(ActionEvent e) {
public void actionPerformed(ActionEvent e) {
try {

byte[] data = resolveDataForDiff();
if (data == null)
return;
if (isDiff) {

Diff.getInstance().clearAsOriginal();
Expand All @@ -348,9 +430,9 @@ public void actionPerformed(ActionEvent e) {
}
}
isDiff = true;
Diff.getInstance().markAsOriginal(tabs.getRaw().getData());
DiffBinary.getInstance().markAsOriginal(tabs.getBinary().getData());
DiffJson.getInstance().markAsOriginal(tabs.getJson().getData());
Diff.getInstance().markAsOriginal(data);
DiffBinary.getInstance().markAsOriginal(data);
DiffJson.getInstance().markAsOriginal(data);
if (GUIHistory.getInstance().containsColor()) {

origColor = GUIHistory.getInstance().getColor();
Expand Down Expand Up @@ -391,30 +473,6 @@ public void mousePressed(MouseEvent e) {
});

charSetCombo.setSelectedItem(charSetUtility.getInstance().getCharSetForGUIComponent());

JPanel diff_panel = new JPanel();
diff_panel.add(diff_orig_button);
diff_panel.add(diff_button);
diff_panel.add(stop_diff_button);
diff_panel.setBorder(new LineBorder(Color.black, 1, true));
diff_panel.setLayout(new BoxLayout(diff_panel, BoxLayout.LINE_AXIS));

JPanel button_panel = new JPanel();
button_panel.add(charSetCombo);
button_panel.add(copy_url_body_button);
button_panel.add(copy_body_button);
button_panel.add(copy_url_button);
button_panel.add(resend_button);
button_panel.add(resend_multiple_button);
button_panel.add(attack_button);
button_panel.add(send_to_resender_button);
button_panel.add(new JLabel(" diff: "));
button_panel.add(diff_panel);
button_panel.setLayout(new BoxLayout(button_panel, BoxLayout.LINE_AXIS));

var button_scroll_pane = createButtonScrollPane(button_panel);
main_panel.add(button_scroll_pane);
return main_panel;
}

public void updateCharSetCombo() {
Expand All @@ -431,12 +489,20 @@ public void updateCharSetCombo() {
}

static JScrollPane createButtonScrollPane(JPanel buttonPanel) {
var scrollBarThickness = UIManager.getInt("ScrollBar.width");
if (scrollBarThickness > 0) {
buttonPanel.setBorder(new EmptyBorder(0, 0, scrollBarThickness, 0));
}
var scrollPane = new JScrollPane(buttonPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
JScrollPane scrollPane = new JScrollPane(buttonPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED) {
@Override
public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
JScrollBar hBar = getHorizontalScrollBar();
// スクロールバーが表示されている場合のみ高さを加算することで、
// 非表示時の余分なスペースを排除しつつ、表示時はレイアウトを押し下げて領域を確保する
if (hBar != null && hBar.isVisible()) {
d.height += hBar.getPreferredSize().height;
}
return d;
}
};
scrollPane.setBorder(null);
scrollPane.getViewport().addComponentListener(new ComponentAdapter() {
@Override
Expand Down Expand Up @@ -469,4 +535,24 @@ public byte[] getData() {
}
return new byte[]{};
}

/**
* マージ行(Request+Response両方ある行)の場合はどちらのデータをDiffに使うか
* ユーザに選択させる。単一パケット行の場合はRequestデータをそのまま返す。 ダイアログでキャンセルされた場合は null を返す。
*/
private byte[] resolveDataForDiff() throws Exception {
if (!GUIHistory.getInstance().isSelectedRowMerged()) {
return tabs.getRaw().getData();
}
// macOS の JOptionPane はボタンを右から左に描画するため、
// 視覚的に左から「Request | Response」の順にするには逆順で定義する。
String[] options = {"Response", "Request"};
int choice = JOptionPane.showOptionDialog(owner, "Which data do you want to use for Diff?",
"Select Diff Target", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, null);
if (choice == JOptionPane.CLOSED_OPTION)
return null;
if (choice == 0)
return GUIPacket.getInstance().getResponsePacket().getReceivedData();
return tabs.getRaw().getData();
}
}
Loading