<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Graham King &#187; Software</title>
	<atom:link href="http://www.darkcoding.net/category/software/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.darkcoding.net</link>
	<description>Solvitas perambulum</description>
	<lastBuildDate>Thu, 02 Feb 2012 04:14:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>In Go, is your terminal a console or a pipe? isatty golang</title>
		<link>http://www.darkcoding.net/software/in-go-is-your-terminal-a-console-or-a-pipe-isatty-golang/</link>
		<comments>http://www.darkcoding.net/software/in-go-is-your-terminal-a-console-or-a-pipe-isatty-golang/#comments</comments>
		<pubDate>Tue, 03 Jan 2012 17:08:20 +0000</pubDate>
		<dc:creator>graham</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[go]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[isatty]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=1334</guid>
		<description><![CDATA[Is my terminal connected to the console, or to a pipe? // Is given File a terminal? func isatty(file *os.File) bool { stat, _ := file.Stat() return !stat.IsFifo() } if ! isatty(os.Stdin) { // read what was piped in } For example: $ ./myprog # isatty = yes $ echo "boo" &#124; ./myprog # isatty [...]]]></description>
			<content:encoded><![CDATA[<p>Is my terminal connected to the console, or to a pipe?</p>

<pre><code>// Is given File a terminal?
func isatty(file *os.File) bool {
    stat, _ := file.Stat()
    return !stat.IsFifo()
}

if ! isatty(os.Stdin) {
  // read what was piped in
}
</code></pre>

<p>For example:</p>

<pre><code>$ ./myprog                # isatty = yes
$ echo "boo" | ./myprog   # isatty = no
</code></pre>

<p>Mirrors the behaviour of Python&#8217;s <a href="http://docs.python.org/library/os.html#os.isatty" target="_blank" class="external">isatty</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/in-go-is-your-terminal-a-console-or-a-pipe-isatty-golang/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Proxy socket.io and nginx on the same port, over SSL</title>
		<link>http://www.darkcoding.net/software/proxy-socket-io-and-nginx-on-the-same-port-over-ssl/</link>
		<comments>http://www.darkcoding.net/software/proxy-socket-io-and-nginx-on-the-same-port-over-ssl/#comments</comments>
		<pubDate>Thu, 22 Dec 2011 22:32:26 +0000</pubDate>
		<dc:creator>graham</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[haproxy]]></category>
		<category><![CDATA[https]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[node]]></category>
		<category><![CDATA[socket.io]]></category>
		<category><![CDATA[ssl]]></category>
		<category><![CDATA[stunnel]]></category>
		<category><![CDATA[web socket]]></category>

		<guid isPermaLink="false">http://darkcoding.ge1.ca/?p=1343</guid>
		<description><![CDATA[My current project has a realtime part, using socket.io on nodejs, and a web part using django on nginx / gunicorn. Here&#8217;s a setup to put them both on the same port, and make them both go over SSL. I&#8217;m assuming you&#8217;re on Ubuntu. Disclaimer: I got this working last night, so no promises. You&#8217;ll [...]]]></description>
			<content:encoded><![CDATA[<p>My current project has a realtime part, using <a href="http://socket.io/" target="_blank" class="external">socket.io</a> on <a href="http://nodejs.org/" target="_blank" class="external">nodejs</a>, and a web part using <a href="https://www.djangoproject.com/" target="_blank" class="external">django</a> on nginx / gunicorn. Here&#8217;s a setup to put them both on the same port, and make them both go over SSL. I&#8217;m assuming you&#8217;re on Ubuntu.</p>

<p><em>Disclaimer</em>: I got this working last night, so no promises. You&#8217;ll certainly want to tweak haproxy&#8217;s config for performance. I also only tested it with socket.io&#8217;s web socket transport.</p>

<h2>Overview</h2>

<p><a href="http://www.darkcoding.net/files/2011/12/socketio_web_same_port.png"><img src="http://www.darkcoding.net/files/2011/12/socketio_web_same_port.png" alt="" title="socketio_web_same_port" width="600" height="120" class="alignnone size-full wp-image-1344" /></a></p>

<ul>
<li><a href="http://www.stunnel.org/" target="_blank" class="external">stunnel</a> decrypts the ssl, so everything after that doesn&#8217;t know about it. It decrypts both web traffic (HTTPS to HTTP), and web socket traffic (WSS to WS).</li>
<li><a href="http://haproxy.1wt.eu/" target="_blank" class="external">haproxy</a> sends web socket traffic to node and web traffic to nginx.</li>
<li>node runs socket.io, handling the web socket traffic.</li>
<li>nginx serves static content.</li>
<li>gunicorn runs python / django, and there&#8217;s a database out back somewhere, but that&#8217;s not relevant here.</li>
</ul>

<p>Currently nginx doesn&#8217;t <span id="more-1343"></span> support HTTP/1.1 for it&#8217;s backends, so it can&#8217;t proxy web socket traffic. That&#8217;s why we have haproxy.<br />
But haproxy doesn&#8217;t do SSL, that&#8217;s why we have stunnel.<br />
And haproxy isn&#8217;t a web server, so we still need nginx.</p>

<h2>Generate a self-signed cert</h2>

<p>To test this you&#8217;ll need an SSL certificate. Here&#8217;s how (thanks <a href="http://www.phpmag.ru/2009/08/12/how-to-create-self-signed-ssl-certificate/" target="_blank" class="external">Victor Farazdagi</a>):</p>

<pre><code>openssl genrsa -out mysite.key 1024
openssl req -new -key mysite.key -out mysite.csr  # common name == your domain
openssl x509 -req -days 365 -in mysite.csr -signkey mysite.key -out mysite.crt
</code></pre>

<h2>stunnel</h2>

<p>Install: <code>sudo apt-get install stunnel4</code>.<br />
Enable it by editing <code>/etc/default/stunnel</code> and settings <code>ENABLED=1</code>.</p>

<p>Config: /etc/stunnel/stunnel.conf</p>

<pre><code>cert = /etc/stunnel/localhost.crt
key = /etc/stunnel/localhost.key

debug = 5
output = /var/log/stunnel4/stunnel.log

[https]
accept = 443
connect = 81
TIMEOUTclose = 0
</code></pre>

<h2>haproxy</h2>

<p>Install: <code>sudo apt-get install haproxy</code></p>

<p>Config: /etc/haproxy/haproxy.cfg</p>

<pre><code>global
    maxconn 4096
    daemon

defaults
    mode http
    log 127.0.0.1 local1 debug
    option httplog

frontend all 0.0.0.0:81
    timeout client 86400000
    default_backend www_backend
    acl is_websocket hdr(Upgrade) -i WebSocket
    acl is_websocket path_beg /socket.io/

    use_backend socket_backend if is_websocket

backend www_backend
    balance roundrobin
    option forwardfor # This sets X-Forwarded-For
    option httpclose
    timeout server 30000
    timeout connect 4000
    server server1 localhost:82 weight 1 maxconn 1024 check

backend socket_backend
    balance roundrobin
    option forwardfor # This sets X-Forwarded-For
    option httpclose
    timeout queue 5000
    timeout server 86400000
    timeout connect 86400000
    server server1 localhost:9000 weight 1 maxconn 1024 check
</code></pre>

<p>haproxy logs to syslog, and expects it to be in server mode, so you need to set that up too (thanks <a href="http://kevin.vanzonneveld.net/techblog/article/haproxy_logging/" target="_blank" class="external">Kevin van Zonneveld</a>):</p>

<p>rsyslog config: /etc/rsyslog.d/haproxy.conf</p>

<pre><code>$ModLoad imudp
$UDPServerRun 514
$UDPServerAddress 127.0.0.1

local1.* -/var/log/haproxy_1.log
&amp; ~ 
</code></pre>

<p>Then bounce rsyslog: <code>sudo restart rsyslog</code></p>

<h2>nginx</h2>

<p>First bounce http traffic to https: /etc/nginx/sites-enabled/default</p>

<pre><code>server {
  listen 80;
  server_name _; # Catch requests that don't match any other server name
  rewrite ^ https://myapp.example.com$request_uri? permanent;
}
</code></pre>

<p>Next setup nginx on port 82, and make sure to rewrite Location responses (see THIS ONE below):</p>

<pre><code>server {
  listen 82;
  server_name myapp.example.com;

  location /static {
    root /var/www/myapp.example.com;
  }

  location / {
    proxy_pass http://unix:/tmp/ginger-gunicorn.sock;
    ## THIS ONE ##
    proxy_redirect http://myapp.example.com https://myapp.example.com;
    ## END THIS ONE ##
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}
</code></pre>

<h2>Node</h2>

<p>Put node on port 9000, with a standard config. Make sure to ask the client library to connect securely, so that it stays on port 443 (https then wss):</p>

<pre><code>var socket = io.connect('https://myapp.example.com', {secure: true})
</code></pre>

<h2>Good luck</h2>

<p>This setup is working for me, so far. There&#8217;s quite a few moving parts. HTTP/1.1 is coming to nginx (it&#8217;s in the dev version already), so hopefully we&#8217;ll be able to use that instead of haproxy and stunnel soon.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/proxy-socket-io-and-nginx-on-the-same-port-over-ssl/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Pretty command line / console output on Unix in Python and Go Lang</title>
		<link>http://www.darkcoding.net/software/pretty-command-line-console-output-on-unix-in-python-and-go-lang/</link>
		<comments>http://www.darkcoding.net/software/pretty-command-line-console-output-on-unix-in-python-and-go-lang/#comments</comments>
		<pubDate>Wed, 16 Nov 2011 01:17:10 +0000</pubDate>
		<dc:creator>graham</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[ansi]]></category>
		<category><![CDATA[commandline]]></category>
		<category><![CDATA[console]]></category>
		<category><![CDATA[go]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=1317</guid>
		<description><![CDATA[There are lots of easy ways to improve the output of your command line scripts, without going full curses, such as single-line output, using bold text and colors, and even measuring the screen width and height. The examples are in Python, with a summary example in Go (golang) at the end. Single line with \r [...]]]></description>
			<content:encoded><![CDATA[<p>There are lots of easy ways to improve the output of your command line scripts, without going <a href="http://en.wikipedia.org/wiki/Ncurses" target="_blank" class="external">full curses</a>, such as single-line output, using bold text and colors, and even measuring the screen width and height.</p>

<p>The examples are in Python, with a summary example in Go (golang) at the end.</p>

<h2>Single line with \r (carriage return)</h2>

<p>Instead of printing a \n (which most &#8216;print&#8217; methods do by default), print a \r. That sends the cursor back to the beginning of the current line (carriage return), without dropping down to a new line (line feed).</p>

<pre><code>import time, sys
total = 10
for i in range(total):
    sys.stdout.write('%d / %d\r' % (i, total))
    sys.stdout.flush()
    time.sleep(0.5)
print('Done     ')
</code></pre>

<p><span id="more-1317"></span>
We do <code>sys.stdout.flush()</code> to flush the output, otherwise the operating system buffers it and you don&#8217;t see anything until the end. Note also the extra spaces after &#8216;Done&#8217;, to clear previous output. Remove them and you&#8217;ll see why they are needed.</p>

<h2>ANSI escape sequences</h2>

<p><a href="http://en.wikipedia.org/wiki/ANSI_escape_code" target="_blank" class="external">ANSI escape sequences</a> are special non-printable characters you send to the terminal to control it. When you do <code>ls --color</code>, ls is using ANSI escape sequences to pretty things up for you.</p>

<p>When the terminal receives an ESC character (octal 33 &#8211; see <code>man ascii</code>) it expects control information to follow, instead of printable data. Most escape sequences start with <code>ESC</code> and <code>[</code>.</p>

<h3>Bold</h3>

<p>To display something in bold, switch on the bright attribute (1), then reset it afterwards (0).</p>

<pre><code>def bold(msg):
    return u'\033[1m%s\033[0m' % msg

print('This is %s yes it is' % bold('bold'))
</code></pre>

<h3>Color</h3>

<p>There are 8 colors, ANSI codes 30 to 37, which can have the bold modifier, making 16 colors. Colors are <a href="http://en.wikipedia.org/wiki/ANSI_escape_code#Colors" target="_blank" class="external">surprisingly consistent across terminals</a>, but can usually be changed by the user.</p>

<pre><code>def color(this_color, string):
    return "\033[" + this_color + "m" + string + "\033[0m"

for i in range(30, 38):
    c = str(i)
    print('This is %s' % color(c, 'color ' + c))

    c = '1;' + str(i)
    print('This is %s' % color(c, 'color ' + c))
</code></pre>

<p>If that's not enough for you, you can have 256 colors (thanks <a href="http://lucentbeing.com/blog/that-256-color-thing/" target="_blank" class="external">lucentbeing</a>), but only if your terminal is set to 256 color mode. You usually get 256 colors by putting one of these lines in your <code>.bashrc</code>:</p>

<pre><code>export TERM='screen-256color'   # Use this is you use tmux or screen. Top choice!
export TERM='xterm-256color'    # Use this otherwise
</code></pre>

<p>Here's lots of colors (copy <code>color</code> function from above):</p>

<pre><code>for i in range(256):
    c = '38;05;%d' % i
    print( color(c, 'color ' + c) )
</code></pre>

<h3>Clear the screen</h3>

<p>A pair of ANSI escape sequences:</p>

<pre><code>import sys

def clear():
    """Clear screen, return cursor to top left"""
    sys.stdout.write('\033[2J')
    sys.stdout.write('\033[H')
    sys.stdout.flush()

clear()
</code></pre>

<h3>More ANSI power</h3>

<p>You can do lots more with ANSI escape sequences, such as change the background color, move the cursor around, and even make it blink. Use your power wisely.</p>

<h2>Measure the screen</h2>

<p>You can find out how many characters wide or lines high the current terminal is. From the command line, try <code>tput lines</code> or <code>tput cols</code>. To get the data direct from Unix:</p>

<pre><code>import sys
import fcntl
import termios
import struct

lines, cols = struct.unpack('hh',  fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, '1234'))
print('Terminal is %d lines high by %d chars wide' % (lines, cols))
</code></pre>

<p>This is making a system call, hence it's a bit inscrutable. It's requesting TIOCGWINSZ information, for file sys.stdout (the terminal), and then interprets it as two packed short integers (hh). The '1234' is just placeholder, which must be four chars long.</p>

<h2>A pretty progress bar</h2>

<p>Putting some of the above together, here's a progress bar.</p>

<pre><code>import sys
import fcntl
import termios
import struct
import time

COLS = struct.unpack('hh',  fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, '1234'))[1]

def bold(msg):
    return u'\033[1m%s\033[0m' % msg

def progress(current, total):
    prefix = '%d / %d' % (current, total)
    bar_start = ' ['
    bar_end = '] '

    bar_size = COLS - len(prefix + bar_start + bar_end)
    amount = int(current / (total / float(bar_size)))
    remain = bar_size - amount

    bar = 'X' * amount + ' ' * remain
    return bold(prefix) + bar_start + bar + bar_end

NUM = 100
for i in range(NUM + 1):
    sys.stdout.write(progress(i, NUM) + '\r')
    sys.stdout.flush()
    time.sleep(0.05)
print('\n')
</code></pre>

<p>That's it! Happy console scripts.</p>

<h1>And now the progress bar in Go <a href="http://golang.org" target="_blank" class="external">golang</a>, for fun</h1>

<pre><code>package main

import (
    "time"
    "os"
    "strconv"
    "strings"
    "syscall"
    "unsafe"
)

const (
    ONE_MSEC    = 1000 * 1000
    _TIOCGWINSZ = 0x5413    // On OSX use 1074295912. Thanks zeebo
    NUM         = 50
)

func main() {

    var bar string

    cols := TerminalWidth()

    for i := 1; i &lt;= NUM; i++ {
        bar = progress(i, NUM, cols)
        os.Stdout.Write([]byte(bar + "\r"))
        os.Stdout.Sync()
        time.Sleep(ONE_MSEC * 50)
    }
    os.Stdout.Write([]byte("\n"))
}

func Bold(str string) string {
    return "\033[1m" + str + "\033[0m"
}

func TerminalWidth() int {
    sizeobj, _ := GetWinsize()
    return int(sizeobj.Col)
}

func progress(current, total, cols int) string {
    prefix := strconv.Itoa(current) + " / " + strconv.Itoa(total)
    bar_start := " ["
    bar_end := "] "

    bar_size := cols - len(prefix + bar_start + bar_end)
    amount := int(float32(current) / (float32(total) / float32(bar_size)))
    remain := bar_size - amount

    bar := strings.Repeat("X", amount) + strings.Repeat(" ", remain)
    return Bold(prefix) + bar_start + bar + bar_end
}

type winsize struct {
    Row    uint16
    Col    uint16
    Xpixel uint16
    Ypixel uint16
}

func GetWinsize() (*winsize, os.Error) {
    ws := new(winsize)

    r1, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
        uintptr(syscall.Stdin),
        uintptr(_TIOCGWINSZ),
        uintptr(unsafe.Pointer(ws)),
    )

    if int(r1) == -1 {
        return nil, os.NewSyscallError("GetWinsize", int(errno))
    }
    return ws, nil
}
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/pretty-command-line-console-output-on-unix-in-python-and-go-lang/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>On why hackers don&#039;t work on large teams</title>
		<link>http://www.darkcoding.net/society/on-why-hackers-dont-work-on-large-teams/</link>
		<comments>http://www.darkcoding.net/society/on-why-hackers-dont-work-on-large-teams/#comments</comments>
		<pubDate>Thu, 03 Nov 2011 00:05:10 +0000</pubDate>
		<dc:creator>graham</dc:creator>
				<category><![CDATA[Society]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=1313</guid>
		<description><![CDATA[We&#8217;ve know for over 35 years that &#8220;adding manpower to a late software project makes it later&#8221;. Amazon has it&#8217;s two-pizza team heuristic: &#8220;If a project team can eat more than two pizzas, it&#8217;s too large&#8221;. The excellent Code Complete has a detailed explanation of how communication costs increase with team size. Yet we still [...]]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve know for over 35 years that <a href="http://en.wikipedia.org/wiki/The_Mythical_Man-Month" target="_blank" class="external">&#8220;adding manpower to a late software project makes it later&#8221;</a>. Amazon has it&#8217;s two-pizza team heuristic: <a href="http://www.google.ca/search?q=two+pizza+team" target="_blank" class="external">&#8220;If a project team can eat more than two pizzas, it&#8217;s too large&#8221;</a>. The excellent <a href="http://www.amazon.com/gp/product/0735619670/ref=as_li_ss_tl?ie=UTF8&#038;tag=darkcoding-20&#038;linkCode=as2&#038;camp=217145&#038;creative=399369&#038;creativeASIN=0735619670">Code Complete</a>  has a detailed explanation of how communication costs increase with team size. Yet we still need reminding.</p>

<p>Dhanji R. Prasanna has an excellent <a href="http://rethrick.com/#mmm" target="_blank" class="external">retrospective on his time on the Google Wave team</a>. He sums up the problem with big teams very well:</p>

<blockquote>
  <p>And this is the essential broader point&#8211;as a programmer you must have a series of wins, every single day. It is the Deus Ex Machina of hacker success. It is what makes you eager for the next feature, and the next after that. And a large team is poison to small wins. The nature of large teams is such that even when you do have wins, they come after long, tiresome and disproportionately many hurdles. And this takes all the wind out of them.</p>
</blockquote>

<p>For me, that&#8217;s really the crux of it. As a programmer, it kills you to not get stuff done. Large teams necessarily involve more communication, more complexity, and less getting stuff done. Large teams are a programmers equivalent of retirement.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/society/on-why-hackers-dont-work-on-large-teams/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Ad-blocking your iPad</title>
		<link>http://www.darkcoding.net/software/ad-blocking-your-ipad/</link>
		<comments>http://www.darkcoding.net/software/ad-blocking-your-ipad/#comments</comments>
		<pubDate>Tue, 25 Oct 2011 00:01:54 +0000</pubDate>
		<dc:creator>graham</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[ipad]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[proxy]]></category>
		<category><![CDATA[squid]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=1288</guid>
		<description><![CDATA[Monitoring iPad network traffic, and blocking advert download. An article on monitoring iPhone traffic by Craig Dunn got me wondering what the iPad is sending over the wire. That led me to blocking many of the adverts apps show. Here&#8217;s how. 1. Setup a proxy (squid on ubuntu) First you need to setup a proxy, [...]]]></description>
			<content:encoded><![CDATA[<h2>Monitoring iPad network traffic, and blocking advert download.</h2>

<p>An article on <a href="http://conceptdev.blogspot.com/2009/01/monitoring-iphone-web-traffic-with.html" target="_blank" class="external">monitoring iPhone traffic by Craig Dunn</a> got me wondering what the iPad is sending over the wire. That led me to blocking many of the adverts apps show. Here&#8217;s how.</p>

<h3>1. Setup a proxy (squid on ubuntu)</h3>

<p>First you need to setup a proxy, and send all your iPad&#8217;s network traffic through that. On Ubuntu <a href="https://help.ubuntu.com/community/Squid" target="_blank" class="external">squid</a> is easy to setup: <code>sudo apt-get install squid</code></p>

<p><span id="more-1288"></span></p>

<p>Edit the config file in <code>/etc/squid/squid.conf</code> and comment in or out the following lines:</p>

<pre><code># Comment in - change if your local IP's don't start with 192.168
acl localnet src 192.168.0.0/16

# Comment out
#http_access deny CONNECT !SSL_ports

# Comment in
http_access allow localnet

# Make sure you have this line
access_log /var/log/squid/access.log squid

# Add this line in the log_fqdn section
log_fqdn on

# Set to off
strip_query_terms off

# We'll play with this later
hosts_file /etc/hosts
</code></pre>

<p>Restart squid: <code>sudo restart squid</code>.
Tail the log file: <code>sudo tail -f /var/log/squid/access.log</code></p>

<h3>2. Set your iPad to use the proxy</h3>

<p>On your iPad go into <code>Settings</code> then <code>Wi-Fi</code>, and tap the white / blue arrow by your network. In the <code>HTTP Proxy</code> section switch to <code>Manual</code> and type in the local IP address (192.168&#8230;) of your machine running squid, and port <code>3128</code> (squid&#8217;s default).</p>

<p>Open an app. You should see some traffic come through in your tail of the log. Not all apps use the network, but most do.</p>

<h3>3. View the POST requests (tcpdump)</h3>

<p>Your squid log will show the GET requests, but not the data of the POST request. You&#8217;re probably seeing lots of this: <code>POST http://data.flurry.com/aap.do</code>. <a href="http://www.flurry.com/" target="_blank" class="external">Flurry</a> seems to be the <a href="http://en.wikipedia.org/wiki/DoubleClick#Criticism" target="_blank" class="external">DoubleClick</a> of iOS.</p>

<p>To view the POST contents, you need tcpdump: <code>sudo apt-get install tcpdump</code>. Then monitor TCP packets on port 80:</p>

<pre><code>sudo tcpdump -i eth0 -A 'tcp port 80 and (((ip[2:2] - ((ip[0]&amp;0xf)&lt;&lt;2)) - ((tcp[12]&amp;0xf0)&gt;&gt;2)) != 0)'
</code></pre>

<p>(I copied that line from tcpdump&#8217;s man page)</p>

<h3>Examples</h3>

<p>Here&#8217;s what some of the apps on my iPad are sending</p>

<p><strong>Angry Birds</strong></p>

<pre><code>POST http://data.flurry.com/aap.do - DIRECT/216.74.41.14 application/octet-stream
GET http://svrsecure-g2-aia.verisign.com/SVRSecureG2.cer - NONE/- text/plain
GET http://ax.init.itunes.apple.com/bag.xml? - DIRECT/63.80.4.50 text/xml
CONNECT p17-buy.itunes.apple.com:443 - DIRECT/17.154.66.17 -
</code></pre>

<p><strong>Talking Tom</strong></p>

<pre><code>POST http://data.flurry.com/aap.do - DIRECT/216.74.41.14 application/octet-stream
GET http://outfit7-affirmations.appspot.com/rest/v1/news/ipad/talking/en/? - DIRECT/74.125.127.141 application/json
POST http://outfit7-affirmations.appspot.com/rest/talkingFriends/v2/iPad/? - DIRECT/74.125.127.141 application/json
GET http://apps.outfit7.com/static/icon/news/IconNewsFreeA72.png - DIRECT/74.125.127.121 image/png
GET http://apps.outfit7.com/static/buttons/rect/news/RectWhite120.png? - DIRECT/74.125.127.121 image/png
GET http://apps.outfit7.com/rest/talkingFriends/v1/push-notification/put/iPad/? - DIRECT/74.125.127.121 application/json
GET http://apps.outfit7.com/static/icon/news/IconNewsFreeA72.png - DIRECT/74.125.127.121 image/png
GET http://apps.outfit7.com/static/buttons/rect/news/RectWhite120.png? - DIRECT/74.125.127.121 image/png
GET http://apps.outfit7.com/rest/talkingFriends/v1/push-notification/put/iPad/? - DIRECT/74.125.127.121 application/json
GET http://apps.outfit7.com/ad/ad.jsp? - DIRECT/74.125.127.121 application/json
POST http://i.w.inmobi.com/showad.asm? - DIRECT/174.140.140.33 text/html
GET http://www.gstatic.com/afma/sdk-core-v40.js - DIRECT/74.125.127.120 text/javascript
GET http://googleads.g.doubleclick.net/mads/gma? - DIRECT/74.125.127.154 text/html
GET http://media.admob.com/gmsg.js - DIRECT/74.125.127.120 text/javascript
GET http://mmv.admob.com/p/i/4d/59/4d59f1cc8bcdb1e30dfa346b08195126-728x90.png - DIRECT/63.80.242.35 image/png
</code></pre>

<p>Here&#8217;s Talking Tom&#8217;s POST request to flurry.</p>

<p>For some reason data.flurry.com&#8217;s IP reverses to perureisen.com, a seemingly unrelated travel site. Maybe they share some virtual hosting.</p>

<p><code>turok</code> is the name of my machine running the proxy. Notice the string <code>IPHONEf0842dbXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXbce</code> (I obscured part of it). That seems to be a unique id for my iPad, which all post&#8217;s to flurry share, and is presumably used to track me across apps.</p>

<p>Here&#8217;s the POST:</p>

<pre><code>turok.local.39475 &gt; perureisen.com.www
POST /aap.do HTTP/1.0
Host: data.flurry.com
User-Agent: TalkingCatIpad/1.7 CFNetwork/485.13.9 Darwin/11.0.0
Content-Type: application/octet-stream
Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Pragma: no-cache
Content-Length: 501
Via: 1.1 turok:3128 (squid/2.7.STABLE9)
X-Forwarded-For: 192.168.1.79
Cache-Control: max-age=259200
Connection: keep-alive

turok.local.39475 &gt; perureisen.com.www
E...h.@.@......@.J)..3.P....j.G7P.9.W....................0...3......R8THSKSDBS4YZQTXWLDP..1.7....IPHONEf0842dbXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXbce...1..z....
3........device.model.1..iPad1,1......1.7...3.._.........GridPageShown.....mericaUITouches......AppLaunched........AppLaunched....launchedVia..
GridPageShown....group..TalkingFriends.................GridClose.........f......... UITouches....touches..head.......4.................
</code></pre>

<p>Some of these POST&#8217;s to data.flurry.com include my location. The response is just an empty 200.</p>

<p>Most of the apps&#8217;s network traffic looks like those two, although sometimes they do weird things. An app called My Horse does this:</p>

<p><strong>My Horse</strong></p>

<pre><code>POST http://data.flurry.com/aap.do - NONE/- text/html
GET http://ax.init.itunes.apple.com/bag.xml?ix=2 - DIRECT/63.80.4.24 text/xml
GET http://itunes.apple.com/WebObjects/MZStore.woa/wa/fetchSoftwareAddOns?appAdamId=421167112&amp;bvrs=1.1.2&amp;sfId=143441-1,9&amp;offerNames=com.naturalmotion.h0rs3.110Gems,com.naturalmotion.h0rs3.1200Gems,com.naturalmotion.h0rs3.20Gems,com.naturalmotion.h0rs3.224Gems,com.naturalmotion.h0rs3.2500Gems,com.naturalmotion.h0rs3.42Gems,com.naturalmotion.h0rs3.450Gems&amp;bid=com.naturalmotion.h0rs3&amp;icuLocale=en_US&amp;appExtVrsId=4160270 - DIRECT/63.80.4.65 text/xml
GET http://svrsecure-g2-aia.verisign.com/SVRSecureG2.cer - NONE/- text/plain
CONNECT p17-buy.itunes.apple.com:443 - DIRECT/17.154.66.17 -
PUT http://mf-horse-upload-rel.s3.amazonaws.com/audit/uid=1345522/directoryListing.txt - DIRECT/207.171.189.81 -
PUT http://mf-horse-upload-rel.s3.amazonaws.com/logFiles/uid=1345522/Fri_Sep_23_12:58:26_2011_144189.txt - DIRECT/207.171.189.81 -
PUT http://mf-horse-upload-rel.s3.amazonaws.com/logFiles/uid=1345522/Fri_Sep_23_16:30:00_2011_375253.txt - DIRECT/207.171.189.81 -
PUT http://mf-horse-upload-rel.s3.amazonaws.com/logFiles/uid=1345522/Sat_Oct__1_17:26:19_2011_317371.txt - DIRECT/207.171.189.81 -
[snip - lots and lots more logFiles PUTing]
</code></pre>

<p>Pretty weird huh? I suspect someone forgot to remove their debugging code, or else they are being exceptionally nosy.</p>

<h3>Ad Blocking, privacy enhancement</h3>

<p>You&#8217;ll notice a few apps hitting ad servers, then downloading advert images. You probably also don&#8217;t want all that monitoring nonsense. The best way to block those requests would be to keep running your iPad through a proxy, and use <code>/etc/hosts</code> to set relevant sites to 127.0.0.1. That requires a local PC running all the time, so instead I used a setting on my router to block certain sites.</p>

<p>Here&#8217;s a list of sites I&#8217;m blocking:</p>

<ul>
<li>data.flurry.com</li>
<li>mob.adwhirl.com</li>
<li>media.admob.com</li>
<li>mmv.admob.com</li>
<li>a.admob.com</li>
<li>i.w.inmobi.com</li>
<li>iphone.playhaven.com</li>
<li>ingameads.gameloft.com</li>
<li>ads.mobclix.com</li>
<li>s.mobclix.com</li>
<li>thisadworks.com</li>
<li>c33.smaato.net</li>
<li>c26.smaato.net</li>
<li>i.xx.openx.com</li>
<li>ads.mp.mydas.mobi</li>
</ul>

<p>All apps still work fine. On one I get the words &#8216;Request failed&#8217; where the advert usually appears.</p>

<p>Some of the apps (Tap Zoo for example) seem to download their state from the server, which comes as a cleartext JSON response. Using your proxy you might be able to substitute your own JSON and change things in interesting ways.</p>

<p>Happy blocking!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/ad-blocking-your-ipad/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Finding memory leaks in Python with objgraph</title>
		<link>http://www.darkcoding.net/software/finding-memory-leaks-in-python-with-objgraph/</link>
		<comments>http://www.darkcoding.net/software/finding-memory-leaks-in-python-with-objgraph/#comments</comments>
		<pubDate>Tue, 27 Sep 2011 00:37:39 +0000</pubDate>
		<dc:creator>graham</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=1271</guid>
		<description><![CDATA[After a frustrating time trying to find a memory leak in my Python code with guppy / heappy hpy, I tried objgraph and, wow, it makes it so easy! Here&#8217;s what you do: pip install objgraph At the relevant point in your code, add an import pdb; pdb.set_trace() to drop into the debugger. Then just [...]]]></description>
			<content:encoded><![CDATA[<p>After a frustrating time trying to find a memory leak in my Python code with guppy / heappy hpy, I tried <a href="http://mg.pov.lt/objgraph/" target="_blank" class="external">objgraph</a> and, wow, it makes it so easy! Here&#8217;s what you do:</p>

<pre><code>pip install objgraph
</code></pre>

<p>At the relevant point in your code, add an <code>import pdb; pdb.set_trace()</code> to drop into the debugger. Then just follow the docs on <a href="http://mg.pov.lt/objgraph/#memory-leak-example" target="_blank" class="external">finding memory leaks with objgraph</a>. In short you do:</p>

<pre><code>&gt;&gt; import objgraph
&gt;&gt; objgraph.show_growth(limit=10)   # Start counting
</code></pre>

<p>Ignore that output. Call the function that leaks memory, iterate once through you loop, whatever you need to do to make your program consume more memory. Now call show_growth again:</p>

<pre><code>&gt;&gt; my_leaky_func()
&gt;&gt; objgraph.show_growth(limit=10)   # Stop and show change
</code></pre>

<p>This time it shows the difference between now and the last time you called it. Those extra objects are the problem.</p>

<p>Finally you need to find where the reference to those leaky objects is being held:</p>

<pre><code>&gt;&gt; import inspect, random
&gt;&gt; objgraph.show_chain(
...     objgraph.find_backref_chain(
...         random.choice(objgraph.by_type('MyBigFatObject')),
...         inspect.ismodule),
...     filename='chain.png')
</code></pre>

<p>That generates a lovely graph in your current directory, showing the ownership chain. Now wasn&#8217;t that easy? Thanks <a href="http://mg.pov.lt/" target="_blank" class="external">Marius Gedminas</a>!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/finding-memory-leaks-in-python-with-objgraph/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cleaning up old git branches</title>
		<link>http://www.darkcoding.net/software/cleaning-up-old-git-branches/</link>
		<comments>http://www.darkcoding.net/software/cleaning-up-old-git-branches/#comments</comments>
		<pubDate>Fri, 16 Sep 2011 19:14:15 +0000</pubDate>
		<dc:creator>graham</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[branch]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=1239</guid>
		<description><![CDATA[After a while working with git, you end up with lots of branches, especially if you use git-flow inspired feature branches. Here&#8217;s one way to clean them up. For any branch, I want to know whether it has been merged, when the last commit was, and ideally if the matching ticket in our tracker has [...]]]></description>
			<content:encoded><![CDATA[<p>After a while working with git, you end up with lots of branches, especially if you use <a href="http://nvie.com/posts/a-successful-git-branching-model/" target="_blank" class="external">git-flow</a> inspired feature branches. Here&#8217;s one way to clean them up.</p>

<p>For any branch, I want to know whether it has been merged, when the last commit was, and ideally if the matching ticket in our tracker has been closed.</p>

<p>Switch to your main branch, usually develop or master: <code>git checkout develop</code></p>

<p>List all the branches which have been fully merged into it:</p>

<pre><code>git branch -a --merged
</code></pre>

<p><span id="more-1239"></span></p>

<p>Show last commit date for a branch:</p>

<pre><code>git log -1 --pretty=format:"%Cgreen%ci %Cred%cr%Creset" &lt;branch_name&gt;
</code></pre>

<p>Putting all that together, for all branches, gives us (thanks <a href="http://www.commandlinefu.com/commands/view/2345/show-git-branches-by-date-useful-for-showing-active-branches" target="_blank" class="external">brunost and unixmonkey7740</a>):</p>

<pre><code>for k in $(git branch -a --merged|grep -v "\-&gt;"|sed s/^..//);do echo -e $(git log -1 --pretty=format:"%Cgreen%ci %Cred%cr%Creset" "$k")\\t"$k";done|sort|more
</code></pre>

<p>Most of my branches are named <code>features/&lt;ticket-id&gt;</code>, so I bring up the above list in one window, and the ticket tracker in a browser, and work through them.</p>

<p>To delete a branch:</p>

<pre><code># If you have it checked out locally
git branch -d &lt;branch_name&gt;

# Delete remote branch
git push origin :&lt;branch_name&gt;
</code></pre>

<p>To keep you safe from typos, <code>git branch -d</code> will refuse to delete branches that haven&#8217;t been merged.</p>

<p>Finally, once you have deleted all the old branches, your colleagues will need to bring their remote names in sync:</p>

<pre><code>git remote prune origin
</code></pre>

<p>Remember that in git branches are just like symlinks, so you&#8217;re not losing any commits by deleting a branch, you&#8217;re just losing a named reference to a commit.</p>

<p>Happy pruning!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/cleaning-up-old-git-branches/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Profiling Django for CPU bound apps</title>
		<link>http://www.darkcoding.net/software/profiling-django-for-cpu-bound-apps/</link>
		<comments>http://www.darkcoding.net/software/profiling-django-for-cpu-bound-apps/#comments</comments>
		<pubDate>Thu, 15 Sep 2011 19:24:17 +0000</pubDate>
		<dc:creator>graham</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=1231</guid>
		<description><![CDATA[For most Django apps, indeed most webapps, the bottleneck is the database. The biggest gains usually come from reducing the number of queries used, and adding database indexes. django-debug-toolbar helps a lot here. After that, caching and de-normalization also help reduce database queries. But what if your app is CPU bound? How do you find [...]]]></description>
			<content:encoded><![CDATA[<p>For most Django apps, indeed most webapps, the bottleneck is the database. The biggest gains usually come from reducing the number of queries used, and adding database indexes. <a href="https://github.com/django-debug-toolbar/django-debug-toolbar" target="_blank" class="external">django-debug-toolbar</a> helps a lot here. After that, caching and de-normalization also help reduce database queries.</p>

<p>But what if your app is CPU bound? How do you find out where it&#8217;s spending it&#8217;s time? It&#8217;s easy with the <a href="http://packages.python.org/django-extensions/runprofileserver.html" target="_blank" class="external">runprofileserver</a> from <a href="http://packages.python.org/django-extensions/" target="_blank" class="external">django-extensions</a> &#8211; here&#8217;s how:</p>

<p><span id="more-1231"></span></p>

<pre><code>pip install django-extensions
</code></pre>

<p>Add <code>django_extensions</code> to your INSTALLED_APPS, then run the profiling server.</p>

<pre><code>django-admin.py runprofileserver
</code></pre>

<p>In your browser, hit the url you&#8217;re interested in. <code>runprofileserver</code> will create hotshot files in /tmp/. Ctrl-C out of your server, and fire up ipython.</p>

<pre><code>from hotshot import stats
p = stats.load('/tmp/&lt;your filename here&gt;')

# In order that functions got called
p.sort_stats('cumulative').print_stats(20)

# Slowest method first
p.sort_stats('time').print_stats(20)
</code></pre>

<p>Here <code>p</code> is a <a href="http://docs.python.org/library/profile.html#instant-user-s-manual" target="_blank" class="external">pstats</a> object. I prefer the cumulative view. Look down it until you see a big drop in the cumulative time &#8211; that&#8217;s where the time was spent.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/profiling-django-for-cpu-bound-apps/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Unicode in Python 2: Decode in, encode out</title>
		<link>http://www.darkcoding.net/software/unicode-in-python-2-decode-in-encode-out/</link>
		<comments>http://www.darkcoding.net/software/unicode-in-python-2-decode-in-encode-out/#comments</comments>
		<pubDate>Wed, 10 Aug 2011 23:24:40 +0000</pubDate>
		<dc:creator>graham</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[unicode]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=1204</guid>
		<description><![CDATA[In Python 2 you need to convert between encoded strings and unicode. It&#8217;s easy if you follow these three simple rules: Decode all input strings name = input_name.decode('utf8', 'ignore') You need to decode all input text: filenames, file contents, console input, database contents, socket data, etc. If you are using Django, it already does this [...]]]></description>
			<content:encoded><![CDATA[<p>In Python 2 you need to convert between encoded strings and unicode. It&#8217;s easy if you follow these three simple rules:</p>

<h3>Decode all input strings</h3>

<pre><code>name = input_name.decode('utf8', 'ignore')
</code></pre>

<p>You need to decode <em>all</em> input text: filenames, file contents, console input, database contents, socket data, etc. If you are using Django, it already does this for you, as much as it can.</p>

<p><span id="more-1204"></span>
The trick is figuring out which encoding you have. Here are <a href="http://docs.python.org/library/codecs.html#standard-encodings" target="_blank" class="external">Python&#8217;s supported encodings</a>. The main ones I try are <strong>utf&#95;8</strong>, <strong>latin&#95;1</strong> and <strong>cp1250</strong>.</p>

<h3>Inside, work only with unicode</h3>

<p>That means prefixing strings with &#8216;u&#8217;:</p>

<pre><code>my_thing = u'Something'
</code></pre>

<p>Be careful when concatenating, <em>always</em> use a u:</p>

<pre><code>my_other = part_one + u'-' + part_two
</code></pre>

<h3>Encode all output strings</h3>

<pre><code>output_name = name.encode('utf8')
print(output_name)
</code></pre>

<p>Strings (bytes) have an encoding. The &#8216;ignore&#8217; parameter to <code>decode</code> tells it to throw away any bytes that aren&#8217;t valid UTF8.</p>

<p>Unicode does not have an encoding. Hence you need to decode the byte string, specifying the encoding, to get unicode. When you want to output, you need to encode your unicode back to a byte string, again specifying the encoding.</p>

<p>In Python 3, all strings are Unicode, and happiness fills the land.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/unicode-in-python-2-decode-in-encode-out/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>git: Resolving &#039;git gc&#039; error: cannot lock ref</title>
		<link>http://www.darkcoding.net/software/git-resolving-git-gc-error-cannot-lock-ref/</link>
		<comments>http://www.darkcoding.net/software/git-resolving-git-gc-error-cannot-lock-ref/#comments</comments>
		<pubDate>Wed, 03 Aug 2011 16:37:01 +0000</pubDate>
		<dc:creator>graham</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.darkcoding.net/?p=1191</guid>
		<description><![CDATA[If you get an error like this from git:: Auto packing the repository for optimum performance. You may also run "git gc" manually. See "git help gc" for more information. error: cannot lock ref 'HEAD (xyz's conflicted copy 2011-06-02)' error: cannot lock ref 'refs/heads/master (xyz's conflicted copy 2011-06-02)' error: failed to run reflog You just [...]]]></description>
			<content:encoded><![CDATA[<p>If you get an error like this from git::</p>

<pre><code>Auto packing the repository for optimum performance. You may also
run "git gc" manually. See "git help gc" for more information.
error: cannot lock ref 'HEAD (xyz's conflicted copy 2011-06-02)'
error: cannot lock ref 'refs/heads/master (xyz's conflicted copy 2011-06-02)'
error: failed to run reflog
</code></pre>

<p>You just need to delete the offending files from <code>.git/logs/</code> and run your operation again.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.darkcoding.net/software/git-resolving-git-gc-error-cannot-lock-ref/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

