# Module: AUTOPROXY

## About this module

The AUTOPROXY module implements a Netscape technology: "Automatic proxy configuration through JavaScript".

The general idea is that there exists a special script that the browser loads. Every time the browser tries to load a URL, the script is run and returns information about how to go about fetching the document at that URL: whether to connect directly to the host, or to connect through a proxy. In the latter case, the script also returns information about the proxy to use.

The technology is popular in large companies, which can use it to balance traffic among multiple proxies or to different proxies at different times of the day.

## Visibility in Opera

### Setting up a proxy script

The proxy script is a JavaScript source file defining a global function `FindProxyForURL` that takes some specific arguments and returns some specific values; it is described in Netscape's documentation.

In the (Windows) preferences UI, go to the "Network" panel, click the "Proxy servers" button, and at the bottom of the dialog check the box for "Use automatic proxy configuration" and enter a URL to the proxy configuration script in the box below it. Then "OK" your way out.

Power users may edit the "[Proxy]" section of opera6.ini, like this:

      [Proxy]
      Use Automatic Proxy Configuration=1
      Automatic Proxy Configuration URL=file:///C:/mydir/myautoproxy.pac

The ".pac" extension is traditional, though other extensions are known (".jsc", for "JavaScript Configuration").

### Interactions inside Opera

If Opera is compiled with `SUPPORT_AUTO_PROXY_CONFIGURATION` (controlled by the feature `FEATURE_AUTO_PROXY_CONFIG`) then the proxy-determining code in `URL_DataStorage` will consult the autoproxy module about a configured proxy for some URLs. Exactly which URLs is determined by clients of this code (ie, the URL module).

### Policies

The following policies are currently in effect, implemented partly by the autoproxy module and partly by the URL module:

- If the autoproxy URL cannot be loaded, then autoproxy functionality is disabled until the autoproxy preferences are changed. (FIXME: It is a known bug that Opera reports that Proxy Configuration will be disabled for the rest of the session. This is the old behaviour)

- If the autoproxy script contains a syntax error, then an error is posted to the JS console with information about the location of the error, autoproxy functionality is disabled until the autoproxy preferences are changed. (FIXME: It should also be reloaded if the script is changed)

- If the autoproxy script throws an unhandled exception at run-time, then the exception is posted to the JS console, autoproxy functionality is disabled until the autoproxy preferences are changed.

- If the autoproxy script accesses network functionality while it is being initialized, then autoproxy functionality is disabled until the autoproxy preferences are changed. (FIXME: It doesn't say why it was disabled; this sounds like a bug.)

- Buggy scripts are handled by cutting them off after a fairly large number of computation steps (currently 10 million steps). A script that fails with an error will cause the URL not to be loaded at all.

## BTS

Autoproxy bugs are flagged with the string `[AUTOPROXY]` in the Summary field and usually have the component "Proxy".

## Interface overview and API documentation

API documentation generated by Doxygen contains all necessary information.

## Implementation description

### Overview

Implementation documentation generated by Doxygen contains what you need to know.

### Footprint

The module is small and can be disabled completely.

Older versions of autoproxy stores the proxy configuration library as a unicode string (probably about 20KB) in the library itself. Newer versions store this library in compiled form, thus reducing footprint.

### Dynamic memory use and OOM handling

(I'm using "UAPCL" as an abbreviation for "URL_AutoProxyConfig_LoadHandler" below.)

OOM policies

  Since the autoproxy module runs as a client of the URL module, it generally uses the same OOM outward signalling mechanisms as the URL module.

  An OOM result is generally signalled through a call on `MemoryManager::RaiseCondition`, while at the same time an error code is posted to the URL layer: either returned as a COMM_REQUEST_FAILED error from `UAPCL::Load()` or posted as an error on that load handler (MSG_COMM_LOADING_FAILED, with error ERR_AUTO_PROXY_CONFIG_FAILED).

  Internally in the module OOM is usually handled by propagating OP_STATUS values.

Who handles OOM?

  Whoever created the load handler and called `UAPCL::Load()` on it must be prepared to handle OOM, signalled as described above. The autoproxy module only performs local cleanup, making sure that things remain consistent and leak-free. Generally the responsibility falls on the URL module.

Flow

  All entry points to the module are through `UAPCL::Load()` and `UAPCL::HandleCallback()`; follow the flow from there, into `UAPCL::ExecuteScript()`. It's straightforward.

  Also see the general implementation description, above.

Heap memory usage

  The module itself allocates very little: only a few small objects to hold data temporarily. But it does use the ECMAScript engine, which can allocate quite a lot. Autoproxy creates one global ES_Runtime, as well as one ES_Context per request, to run requests.

  The autoproxy module caches an ES_Runtime, and this may prevent some memory from being freed even if all windows are closed. See "Caching and freeing memory" below.

Stack memory usage

  The module itself uses no recursive calls and no stack-allocated arrays or large objects. It calls the ECMAScript engine and the URL layer; they may use more. Locally in the module I'd say that less than 50 words of stack memory are live in the worst case on a system with a dense processor stack (like x86).

Static memory usage

  The module currently has three global variables (total of three words of memory).

Caching and freeing memory

  One ES_Runtime is cached by the module and may hold onto some usually not very large amount of memory (though there are no bounds on how much memory a runtime can reference.)

  The caches can be cleaned out by calling the global function `ReleaseAutoProxyResources()`. It is safe to call this function at any time. If there are no pending autoproxy requests, this will release all dynamic memory that is currently held by the module. In order to actually free memory held by the ECMAScript environment, it may be necessary to run a garbage collection; the autoproxy module does not do that.

Freeing memory on exit

  The function `ReleaseAutoProxyResources()` must be called on exit before the ECMAScript environment is destroyed.

Temp buffers

  One of the methods in the autoproxy module is using the buffer returned from `MemoryManager::GetTempBuffer()`. There is no check on whether this buffer is used outside the module. (Reuse of the buffer internally is not a problem: this is the only place in the code that uses the temp buffer, and the code does not call any interesting functions.) It would be trivial to allocate a temporary string on the heap to serve the same purpose.

Memory tuning

  No known opportunities in the module proper.

Tests

  There are no tests, since memory use is fairly trivial.

  If the deallocation function is not called before Opera exits, one will get an assert in the ECMAScript engine when that is destroyed.

Coverage

  Running the selftests for the module (see comments in `testsuite/autoproxy.ot` for detailed description; it is more than running with `-test`) should exercise most of the code.

Design choices

  One trade-off has been made: an ES_Runtime is cached, to avoid having to recreate it for every URL lookup. This means that RAM use is higher than the theoretical minimum. It is probably the case that it is not practical to avoid at least some kind of caching.

  There are no other known tradeoffs.

Improvements

  There are no planned improvements. All known improvements would come as a result of the ECMAScript engine improving its memory use.

### Performance

The performance of the module is largely dependent on the performance of the ECMAScript engine.

The important things that happen in the autoproxy module are:

- Reading the autoproxy script from a URL, creating a runtime for it, and compiling it.

  These operations are performed whenever the script changes.

- Running a FindProxyForURL request. This consists of compiling a small script, creating a context, and running the script for a quantum. If the script finishes within its first quantum (most do) then it takes no penalty; otherwise, it is rescheduled to run in 50ms.

  FIXME: Here the time may well be dominated by invoking the compiler. We can avoid running the compiler by instead looking up the function and calling it directly.

  FIXME: The 50ms reschedule is usually too long; if we could use the standard scheduler we could do better.

- Performing name lookups.

  Name lookups are synchronous for the script that invokes them, but take no time per se except waiting for the lookup to complete.

- Executing proxy library functions: `shExpMatch` and the like.

  `shExpMatch` is written in C and is probably not a bottleneck. The other library functions are written in JavaScript, but they are generally simple and it is not clear they should be a problem. With the new ECMAScript compiler they should be faster still.

## References

ECMAScript module
ECMAScript utilities module
Netscape's original documentation (local copy)
A revised version of Netscape's documentation that include some design choices (local copy)
Module repository page (Wiki)
ECMAScript 3rd edition (external)

$Id$