http.mx raw

   1  package transport
   2  
   3  import "bytes"
   4  
   5  // parseHTTPHeaders parses request-line + headers from a header block (no body).
   6  func parseHTTPHeaders(data []byte) (p *httpReq) {
   7  	lineEnd := bytes.IndexByte(data, '\n')
   8  	if lineEnd < 0 {
   9  		return nil
  10  	}
  11  	line := data[:lineEnd]
  12  	if len(line) > 0 && line[len(line)-1] == '\r' {
  13  		line = line[:len(line)-1]
  14  	}
  15  
  16  	sp1 := bytes.IndexByte(line, ' ')
  17  	if sp1 < 0 {
  18  		return nil
  19  	}
  20  	method := string(makeCopy(line[:sp1]))
  21  	rest := line[sp1+1:]
  22  	sp2 := bytes.IndexByte(rest, ' ')
  23  	var path string
  24  	if sp2 >= 0 {
  25  		path = string(makeCopy(rest[:sp2]))
  26  	} else {
  27  		path = string(makeCopy(rest))
  28  	}
  29  	if qi := bytes.IndexByte([]byte(path), '?'); qi >= 0 {
  30  		path = path[:qi]
  31  	}
  32  
  33  	headers := map[string]string{}
  34  	pos := lineEnd + 1
  35  	for pos < len(data) {
  36  		nlPos := bytes.IndexByte(data[pos:], '\n')
  37  		var hline []byte
  38  		if nlPos < 0 {
  39  			hline = data[pos:]
  40  			pos = len(data)
  41  		} else {
  42  			nlPos += pos
  43  			hline = data[pos:nlPos]
  44  			pos = nlPos + 1
  45  		}
  46  		if len(hline) > 0 && hline[len(hline)-1] == '\r' {
  47  			hline = hline[:len(hline)-1]
  48  		}
  49  		if len(hline) == 0 {
  50  			continue
  51  		}
  52  		colon := bytes.IndexByte(hline, ':')
  53  		if colon >= 0 {
  54  			key := string(toLower(makeCopy(hline[:colon])))
  55  			val := string(makeCopy(trimSpace(hline[colon+1:])))
  56  			headers[key] = val
  57  		}
  58  	}
  59  
  60  	return &httpReq{method: method, path: path, headers: headers}
  61  }
  62  
  63  func parseContentLength(s string) (n int32) {
  64  	if s == "" {
  65  		return 0
  66  	}
  67  	n := 0
  68  	for i := 0; i < len(s); i++ {
  69  		if s[i] < '0' || s[i] > '9' {
  70  			return 0
  71  		}
  72  		n = n*10 + int32(s[i]-'0')
  73  	}
  74  	return n
  75  }
  76  
  77  func buildHTTPResponse(status int32, headers map[string]string, body []byte) (buf []byte) {
  78  	statusText := "OK"
  79  	switch status {
  80  	case 200:
  81  		statusText = "OK"
  82  	case 204:
  83  		statusText = "No Content"
  84  	case 206:
  85  		statusText = "Partial Content"
  86  	case 301:
  87  		statusText = "Moved Permanently"
  88  	case 302:
  89  		statusText = "Found"
  90  	case 304:
  91  		statusText = "Not Modified"
  92  	case 400:
  93  		statusText = "Bad Request"
  94  	case 401:
  95  		statusText = "Unauthorized"
  96  	case 403:
  97  		statusText = "Forbidden"
  98  	case 404:
  99  		statusText = "Not Found"
 100  	case 405:
 101  		statusText = "Method Not Allowed"
 102  	case 415:
 103  		statusText = "Unsupported Media Type"
 104  	case 429:
 105  		statusText = "Too Many Requests"
 106  	case 500:
 107  		statusText = "Internal Server Error"
 108  	case 502:
 109  		statusText = "Bad Gateway"
 110  	case 503:
 111  		statusText = "Service Unavailable"
 112  	case 504:
 113  		statusText = "Gateway Timeout"
 114  	}
 115  
 116  	var buf []byte
 117  	buf = buf | "HTTP/1.1 "
 118  	buf = appendInt(buf, status)
 119  	buf = buf | " "
 120  	buf = buf | statusText
 121  	buf = buf | "\r\n"
 122  	hasCL := false
 123  	for k, v := range headers {
 124  		if k == "Content-Length" {
 125  			hasCL = true
 126  		}
 127  		buf = buf | k
 128  		buf = buf | ": "
 129  		buf = buf | v
 130  		buf = buf | "\r\n"
 131  	}
 132  	if !hasCL {
 133  		buf = buf | "Content-Length: "
 134  		buf = appendInt(buf, len(body))
 135  		buf = buf | "\r\n"
 136  	}
 137  	buf = buf | "Connection: keep-alive\r\n"
 138  	buf = buf | "\r\n"
 139  	buf = buf | body
 140  	return buf
 141  }
 142  
 143  func writeHTTPResponse(fd int32, status int32, headers map[string]string, body []byte) (ok bool) {
 144  	return writeAll(fd, buildHTTPResponse(status, headers, body)) == nil
 145  }
 146  
 147  // DecodeHeaders deserializes null-separated key\0val\0 pairs to a map.
 148  // Used by static worker responses.
 149  func DecodeHeaders(b []byte) (m map[string]string) {
 150  	h := map[string]string{}
 151  	for len(b) > 0 {
 152  		ki := 0
 153  		for ki < len(b) && b[ki] != 0 {
 154  			ki++
 155  		}
 156  		if ki >= len(b) {
 157  			break
 158  		}
 159  		k := string(b[:ki])
 160  		b = b[ki+1:]
 161  		vi := 0
 162  		for vi < len(b) && b[vi] != 0 {
 163  			vi++
 164  		}
 165  		v := string(b[:vi])
 166  		if vi < len(b) {
 167  			b = b[vi+1:]
 168  		} else {
 169  			b = b[vi:]
 170  		}
 171  		h[k] = v
 172  	}
 173  	return h
 174  }
 175  
 176  func appendInt(buf []byte, n int32) (buf2 []byte) {
 177  	if n == 0 {
 178  		return buf | "0"
 179  	}
 180  	if n < 0 {
 181  		buf = buf | "-"
 182  		n = -n
 183  	}
 184  	var digits [20]byte
 185  	i := len(digits)
 186  	for n > 0 {
 187  		i--
 188  		digits[i] = byte('0' + n%10)
 189  		n /= 10
 190  	}
 191  	return buf | digits[i:]
 192  }
 193  
 194  // AppendInt is the exported version used by server for NIP-11 JSON construction.
 195  func AppendInt(buf []byte, n int32) (buf2 []byte) { return appendInt(buf, n) }
 196  
 197  func AppendStr(buf []byte, s string) (buf2 []byte) { return buf | s }
 198  
 199  func AppendJSONString(buf []byte, s string) (buf2 []byte) {
 200  	buf = buf | "\""
 201  	for i := 0; i < len(s); i++ {
 202  		c := s[i]
 203  		switch c {
 204  		case '"':
 205  			buf = buf | "\\\""
 206  		case '\\':
 207  			buf = buf | "\\\\"
 208  		case '\n':
 209  			buf = buf | "\\n"
 210  		case '\r':
 211  			buf = buf | "\\r"
 212  		case '\t':
 213  			buf = buf | "\\t"
 214  		default:
 215  			if c < 0x20 {
 216  				buf = buf | "\\u00"
 217  				buf = buf | string([]byte{hexNibble(c >> 4), hexNibble(c & 0x0f)})
 218  			} else {
 219  				buf = buf | string([]byte{c})
 220  			}
 221  		}
 222  	}
 223  	buf = buf | "\""
 224  	return buf
 225  }
 226  
 227  func hexNibble(b byte) (bv byte) {
 228  	if b < 10 {
 229  		return '0' + b
 230  	}
 231  	return 'a' + (b - 10)
 232  }
 233  
 234  // FirstXFF returns the leftmost IP from an X-Forwarded-For header.
 235  func FirstXFF(xff string) (s string) {
 236  	for i := 0; i < len(xff); i++ {
 237  		if xff[i] == ',' {
 238  			return string(makeCopy(trimSpace([]byte(xff[:i]))))
 239  		}
 240  	}
 241  	return string(makeCopy(trimSpace([]byte(xff))))
 242  }
 243  
 244  // HasPrefix is exported for use by server.
 245  func HasPrefix(s, prefix string) (ok bool) {
 246  	return len(s) >= len(prefix) && s[:len(prefix)] == prefix
 247  }
 248  
 249  func hasPrefix(s, prefix string) (ok bool) {
 250  	return len(s) >= len(prefix) && s[:len(prefix)] == prefix
 251  }
 252  
 253  var botAgents []string
 254  
 255  func isBot(ua string) (ok bool) {
 256  	if ua == "" {
 257  		return false
 258  	}
 259  	for _, bot := range botAgents {
 260  		if bytes.Contains([]byte(ua), []byte(bot)) {
 261  			return true
 262  		}
 263  	}
 264  	return false
 265  }
 266  
 267  func makeCopy(b []byte) (buf []byte) {
 268  	c := []byte{:len(b)}
 269  	copy(c, b)
 270  	return c
 271  }
 272  
 273  func trimSpace(b []byte) (buf []byte) {
 274  	for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') {
 275  		b = b[1:]
 276  	}
 277  	for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\t') {
 278  		b = b[:len(b)-1]
 279  	}
 280  	return b
 281  }
 282  
 283  func toLower(b []byte) (buf []byte) {
 284  	for i := range b {
 285  		if b[i] >= 'A' && b[i] <= 'Z' {
 286  			b[i] = b[i] + 32
 287  		}
 288  	}
 289  	return b
 290  }
 291  
 292  // ParseAddr splits "host:port" into ([4]byte IP, int32 port).
 293  func ParseAddr(addr string) (ip [4]byte, n int32) {
 294  	ab := []byte(addr)
 295  	var ip [4]byte
 296  	colon := -1
 297  	for i := len(ab) - 1; i >= 0; i-- {
 298  		if ab[i] == ':' {
 299  			colon = i
 300  			break
 301  		}
 302  	}
 303  	if colon < 0 {
 304  		return ip, 0
 305  	}
 306  	port := 0
 307  	for i := colon + 1; i < len(ab); i++ {
 308  		port = port*10 + int32(ab[i]-'0')
 309  	}
 310  	host := ab[:colon]
 311  	if len(host) > 0 {
 312  		octet := 0
 313  		idx := 0
 314  		for i := 0; i < len(host); i++ {
 315  			if host[i] == '.' {
 316  				if idx < 4 {
 317  					ip[idx] = byte(octet)
 318  				}
 319  				idx++
 320  				octet = 0
 321  			} else {
 322  				octet = octet*10 + int32(host[i]-'0')
 323  			}
 324  		}
 325  		if idx < 4 {
 326  			ip[idx] = byte(octet)
 327  		}
 328  	}
 329  	return ip, port
 330  }
 331