Android: 移植polipo – a caching web proxy

之前介绍过,polipo可以将http/https请求forward到socks4/5 proxy server ->@<-。polipo是一个小巧,便捷的网站缓存代理软件,它是一个web cache, 一个HTTP  proxy,同时还是一个proxy server。

polipo的一些特性:

  • Polipo will use HTTP/1.1 pipelining if it believes that the remote server supports it, whether the incoming requests are pipelined or come in simultaneously on multiple connections (this is more than the simple usage of persistent connections, which is done by e.g. Squid);
  • Polipo will cache the initial segment of an instance if the download has been interrupted, and, if necessary, complete it later using Range requests;
  • Polipo will upgrade client requests to HTTP/1.1 even if they come in as HTTP/1.0, and up- or downgrade server replies to the client‘s capabilities (this may involve conversion to or from the HTTP/1.1 chunked encoding);
  • Polipo has complete support for IPv6 (except for scoped (link-local) addresses).
  • Polipo can optionally use a technique known as Poor Man‘s Multiplexing to reduce latency even further.

Polipo可以在Windows系统和linux系统上运行,这使得将polipo移植到Android系统上的难度大大降低,可以说是几乎是不费吹灰之力。

2016_03_02_polipo

  • 编译polipo

可以从官网上下载最新版本(polipo-20140107)的源代码:http://www.pps.univ-paris-diderot.fr/~jch/software/files/polipo/

1. 编写Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := libpolipo
LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := \
	atom.c auth.c chunk.c client.c config.c dirent_compat.c \
	diskcache.c dns.c event.c forbidden.c fts_compat.c \
	http.c http_parse.c io.c local.c log.c main.c md5.c \
	mingw.c object.c parse_time.c server.c socks.c \
	tunnel.c util.c

LOCAL_CFLAGS := \
	-DHAS_STDINT_H \
	-DLOCAL_ROOT="\"/sdcard/polipo/www\"" \
	-DDISK_CACHE_ROOT="\"/sdcard/polipo/cache/\"" \
	-Dmain=polipo_main

LOCAL_CFLAGS += \
	-Wno-unused-parameter -Wno-sign-compare

include $(BUILD_SHARED_LIBRARY)

将LOCAL_ROOT(local document root)设置为/sdcard/polipo/www,将disk cache root设置为/sdcard/polipo/cache, 由于编译出来的是so库,将入口函数main()更名为polipo_main()。

2. 修改diskcache.c文件

将validateLocalEntry()函数中的ss.st_mode & S_IROTH, 由于local entry中的文件在/sdcard中,sdcard中的文件ss.st_mode & S_IROTH为false, 使得浏览器访问http://127.0.0.3:8123时,返加404 page no found。

/* Same interface as validateEntry -- see below */
int
validateLocalEntry(ObjectPtr object, int fd,
                   int *body_offset_return, off_t *offset_return)
{
    struct stat ss;
    char buf[512];
    int n, rc;
    char *encoding;

    rc = fstat(fd, &ss);
    if(rc < 0) { 
        do_log_error(L_ERROR, errno, "Couldn't stat");
        return -1;
    }    

    if(S_ISREG(ss.st_mode)) {
        if(/*!(ss.st_mode & S_IROTH) ||*/
           (object->length >= 0 && object->length != ss.st_size) ||
           (object->last_modified >= 0 && 
            object->last_modified != ss.st_mtime))
            return -1;
    } else {
        notifyObject(object);
        return -1;
    }    
    // ...
}
  • 配置polipo

在sdcard中新建/sdcard/polipo/www文件夹,用于存放index.html文件(将localindex.html文件copy到/sdcard/polipo/www/中并更名为index.html)

在sdcard中新建/sdcard/polip/cache文件,用于存放浏览网页时生成的cache文件。

将config.sample文件copy到/sdcard/polipo/中并更名为config,如果这个文件不存大,polipo将无法正常运行。

如果想要查看Known servers与Disk cache index, 需要将config文件中的相关配置项打开:

# Uncomment this if you want to enable the pages under /polipo/index?
# and /polipo/servers?.  This is a serious privacy leak if your proxy
# is shared.

disableIndexing = false
disableServersList = false
  • JNI层

直接调用polipo_main()函数,传入config文件所在的位置:

extern "C" int polipo_main(int argc, const char *argv[]);

extern "C" void Java_com_brobwind_bronil_NativeNil_execPolipo(JNIEnv *, jobject)
{
	const char *argv[] = {
		"com/brobwind/bronil/polipo",
		"-c", "/sdcard/polipo/config",
	};
	polipo_main(sizeof(argv) / sizeof(argv[0]), argv);
}
  • Java层

创建一个service使其一直在后台运行,并创建一个on going notification, 使其能够作为一个foreground service, 能够一直在后台运行。同将点击notification能够弹出配置界面:

public class NilService extends Service {
	// A special ID assigned to this on-going notification.
	private static final int ONGOING_NOTIFICATION = 1248;

	@Override
	public void onCreate() {
		new Thread(new Runnable() {
			@Override
			public void run() {
				NativeNil.execPolipo();
			}
		}).start();

		createNotification();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int id) {
		return START_STICKY;
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		stopForeground(true);
	}

	private void createNotification() {
		Intent notificationIntent = new Intent(Intent.ACTION_VIEW);
		notificationIntent.setData(Uri.parse("http://127.0.0.1:8123"));
		PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
				notificationIntent, 0);
		Notification notification = new Notification.Builder(this).
				setContentTitle("Bro Nil - http://www.brobwind.com").
				setContentText("polipo - The caching web proxy is running").
				setSmallIcon(android.R.drawable.ic_media_play).
				setOngoing(true).
				setContentIntent(pendingIntent).
				getNotification();
		startForeground(ONGOING_NOTIFICATION, notification);
	}
}
  • 配置代理服务器

在关联的wifi配置项中设置代理为http://127.0.0.1:8123。

 

感谢polipo的作者Juliusz Chroboczek提供了这么好的软件。

相关的参考文档:

  1. http://www.pps.univ-paris-diderot.fr/~jch/software/polipo/
  2. http://www.squid-cache.org/

发表评论

电子邮件地址不会被公开。 必填项已用*标注