Skip to content

Instantly share code, notes, and snippets.

@ericlaw1979
Last active November 21, 2023 16:53
Show Gist options
  • Save ericlaw1979/412648cd7bfe887920dd5479f952080a to your computer and use it in GitHub Desktop.
Save ericlaw1979/412648cd7bfe887920dd5479f952080a to your computer and use it in GitHub Desktop.
Trivial Fiddler extension that converts inbound images to JPEG.
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using Fiddler;
using System.Windows.Forms;
using System.Diagnostics;
[assembly: Fiddler.RequiredVersion("2.6.2.0")]
namespace MakeJPEG
{
public class JPEGify: IAutoTamper2
{
private bool bAutoConvert = false;
private long lngAutoJPEGQuality = 80; // 80% quality.
MenuItem oRulesMenuItem = new MenuItem("JPEGify I&mages...");
MenuItem oContextMenuItem = new MenuItem("&JPEGify...");
#region IAutoTamper Members
public void OnPeekAtResponseHeaders(Session oSession)
{
if (!bAutoConvert) return;
if (!oSession.oResponse.headers.ExistsAndContains("Content-Type", "image/")) return;
// Disable streaming because we want to JPEG-convert stuff...
oSession.bBufferResponse = true;
}
public void AutoTamperResponseBefore(Session oSession)
{
if (!bAutoConvert) return;
ProcessSession(oSession, lngAutoJPEGQuality);
}
private void ProcessSession(Session oSession, long lngQuality)
{
if (oSession.responseCode != 200 || !oSession.bHasResponse ||
!oSession.oResponse.headers.ExistsAndContains("Content-Type", "image/")) return;
if (!oSession.oResponse.headers.ExistsAndContains("Content-Type", "gif") && // <-- Should we bail if animated
!oSession.oResponse.headers.ExistsAndContains("Content-Type", "png") &&
!oSession.oResponse.headers.ExistsAndContains("Content-Type", "bmp") &&
!oSession.oResponse.headers.ExistsAndContains("Content-Type", "jp"))
return;
try
{
int iOldSize = oSession.responseBodyBytes.Length;
MakeJPEGOfQuality(oSession, lngQuality);
oSession.oFlags.Add("Size-Before-JPEGify", iOldSize.ToString("N0"));
float flChange = 100 * (oSession.responseBodyBytes.Length / (float)iOldSize);
string sBloatInfo = String.Format("JPEGify to {0:N0} from {1:N0} bytes ({2:N1}%)", oSession.responseBodyBytes.Length, iOldSize, flChange);
FiddlerApplication.UI.SetStatusText(sBloatInfo);
}
catch (Exception eX)
{
FiddlerApplication.Log.LogString(eX.ToString());
}
}
private void MakeJPEGOfQuality(Session oSession, long lngQuality)
{
if ((lngQuality < 1) || (lngQuality > 100)) throw new ArgumentOutOfRangeException("lngQuality", lngQuality, "Quality must be between 1 and 100");
oSession.utilDecodeResponse();
using (MemoryStream oStream = new MemoryStream(oSession.responseBodyBytes))
{
Bitmap oBMP = new Bitmap(oStream);
using (MemoryStream oNewStream = new MemoryStream())
{
// TODO: Can we cache this in a thread-safe manner?
ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg);
// Create an Encoder object based on the GUID for the Quality parameter category.
Encoder myEncoder = Encoder.Quality;
EncoderParameters myEncoderParameters = new EncoderParameters(1);
EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, lngQuality);
myEncoderParameters.Param[0] = myEncoderParameter;
oBMP.Save(oNewStream, jgpEncoder, myEncoderParameters);
oSession.ResponseBody = oNewStream.ToArray();
oSession.oResponse.headers["Content-Type"] = "image/jpeg";
oSession.oResponse.headers["Cache-Control"] = "no-cache";
}
}
}
public void OnBeforeReturningError(Session oSession) {/* no-op */}
public void AutoTamperRequestAfter(Session oSession) {/* no-op */}
public void AutoTamperRequestBefore(Session oSession){/* no-op */}
public void AutoTamperResponseAfter(Session oSession){/* no-op */}
#endregion
private static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
#region IFiddlerExtension Members
public void OnBeforeUnload()
{
bAutoConvert = false;
oRulesMenuItem.Dispose();
oContextMenuItem.Dispose();
}
public void OnLoad()
{
oRulesMenuItem.Click += delegate(object s, EventArgs e)
{
bool bNewValue = !oRulesMenuItem.Checked;
if (bNewValue)
{
string sQuality = frmPrompt.GetUserString("Set Quality", "Enter a value, 1 to 100", lngAutoJPEGQuality.ToString(), true, frmPrompt.PromptIcon.Numbers);
if (String.IsNullOrEmpty(sQuality))
{
bNewValue = false;
}
if (bNewValue)
{
long lng = 0;
if (long.TryParse(sQuality, out lng))
{
lngAutoJPEGQuality = lng;
}
else
{
bNewValue = false;
}
}
}
oRulesMenuItem.Checked = bAutoConvert = bNewValue;
};
oContextMenuItem.Click += delegate(object s, EventArgs e)
{
string sQuality = frmPrompt.GetUserString("Set Quality", "Enter a value, 1 to 100", lngAutoJPEGQuality.ToString(), true, frmPrompt.PromptIcon.Numbers);
if (String.IsNullOrEmpty(sQuality)) return;
long lng = 0;
if (!long.TryParse(sQuality, out lng) || (lng > 100) || (lng < 1)) return;
foreach (Session oS in FiddlerApplication.UI.GetSelectedSessions())
{
ProcessSession(oS, lng);
oS.RefreshUI();
}
FiddlerApplication.UI.SetStatusText("JPEGify Complete");
};
FiddlerApplication.UI.mnuRules.MenuItems.Add(oRulesMenuItem);
FiddlerApplication.UI.lvSessions.ContextMenu.MenuItems.Add(0, oContextMenuItem );
}
#endregion
}
}
@ericlaw1979
Copy link
Author

csc /target:library /r:"C:\Users\ericlaw\AppData\Local\Programs\Fiddler\fiddler.exe" jpegify.cs

@ericlaw1979
Copy link
Author

Pre-built version; put in your Documents\Fiddler2\Scripts folder
https://bayden.com/dl/jpegify.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment