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) *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) int {
  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 + int(s[i]-'0')
  73  	}
  74  	return n
  75  }
  76  
  77  func buildHTTPResponse(status int, headers map[string]string, body []byte) []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 = append(buf, "HTTP/1.1 "...)
 118  	buf = appendInt(buf, status)
 119  	buf = append(buf, ' ')
 120  	buf = append(buf, statusText...)
 121  	buf = append(buf, "\r\n"...)
 122  	hasCL := false
 123  	for k, v := range headers {
 124  		if k == "Content-Length" {
 125  			hasCL = true
 126  		}
 127  		buf = append(buf, k...)
 128  		buf = append(buf, ": "...)
 129  		buf = append(buf, v...)
 130  		buf = append(buf, "\r\n"...)
 131  	}
 132  	if !hasCL {
 133  		buf = append(buf, "Content-Length: "...)
 134  		buf = appendInt(buf, len(body))
 135  		buf = append(buf, "\r\n"...)
 136  	}
 137  	buf = append(buf, "Connection: keep-alive\r\n"...)
 138  	buf = append(buf, "\r\n"...)
 139  	buf = append(buf, body...)
 140  	return buf
 141  }
 142  
 143  func writeHTTPResponse(fd int, status int, headers map[string]string, body []byte) 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) 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 int) []byte {
 177  	if n == 0 {
 178  		return append(buf, '0')
 179  	}
 180  	if n < 0 {
 181  		buf = append(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 append(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 int) []byte { return appendInt(buf, n) }
 196  
 197  func AppendStr(buf []byte, s string) []byte { return append(buf, s...) }
 198  
 199  func AppendJSONString(buf []byte, s string) []byte {
 200  	buf = append(buf, '"')
 201  	for i := 0; i < len(s); i++ {
 202  		c := s[i]
 203  		switch c {
 204  		case '"':
 205  			buf = append(buf, '\\', '"')
 206  		case '\\':
 207  			buf = append(buf, '\\', '\\')
 208  		case '\n':
 209  			buf = append(buf, '\\', 'n')
 210  		case '\r':
 211  			buf = append(buf, '\\', 'r')
 212  		case '\t':
 213  			buf = append(buf, '\\', 't')
 214  		default:
 215  			if c < 0x20 {
 216  				buf = append(buf, '\\', 'u', '0', '0',
 217  					hexNibble(c>>4), hexNibble(c&0x0f))
 218  			} else {
 219  				buf = append(buf, c)
 220  			}
 221  		}
 222  	}
 223  	buf = append(buf, '"')
 224  	return buf
 225  }
 226  
 227  func hexNibble(b byte) 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) 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) bool {
 246  	return len(s) >= len(prefix) && s[:len(prefix)] == prefix
 247  }
 248  
 249  func hasPrefix(s, prefix string) bool {
 250  	return len(s) >= len(prefix) && s[:len(prefix)] == prefix
 251  }
 252  
 253  var botAgents = []string{
 254  	"SemrushBot", "AhrefsBot", "GPTBot", "ClaudeBot",
 255  	"MJ12bot", "DotBot", "PetalBot", "Bytespider",
 256  	"Sogou", "YandexBot", "BLEXBot", "DataForSeoBot",
 257  }
 258  
 259  func isBot(ua string) bool {
 260  	if ua == "" {
 261  		return false
 262  	}
 263  	for _, bot := range botAgents {
 264  		if bytes.Contains([]byte(ua), []byte(bot)) {
 265  			return true
 266  		}
 267  	}
 268  	return false
 269  }
 270  
 271  func makeCopy(b []byte) []byte {
 272  	c := []byte{:len(b)}
 273  	copy(c, b)
 274  	return c
 275  }
 276  
 277  func trimSpace(b []byte) []byte {
 278  	for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') {
 279  		b = b[1:]
 280  	}
 281  	for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\t') {
 282  		b = b[:len(b)-1]
 283  	}
 284  	return b
 285  }
 286  
 287  func toLower(b []byte) []byte {
 288  	for i := range b {
 289  		if b[i] >= 'A' && b[i] <= 'Z' {
 290  			b[i] = b[i] + 32
 291  		}
 292  	}
 293  	return b
 294  }
 295  
 296  // ParseAddr splits "host:port" into ([4]byte IP, int port).
 297  func ParseAddr(addr string) ([4]byte, int) {
 298  	ab := []byte(addr)
 299  	var ip [4]byte
 300  	colon := -1
 301  	for i := len(ab) - 1; i >= 0; i-- {
 302  		if ab[i] == ':' {
 303  			colon = i
 304  			break
 305  		}
 306  	}
 307  	if colon < 0 {
 308  		return ip, 0
 309  	}
 310  	port := 0
 311  	for i := colon + 1; i < len(ab); i++ {
 312  		port = port*10 + int(ab[i]-'0')
 313  	}
 314  	host := ab[:colon]
 315  	if len(host) > 0 {
 316  		octet := 0
 317  		idx := 0
 318  		for i := 0; i < len(host); i++ {
 319  			if host[i] == '.' {
 320  				if idx < 4 {
 321  					ip[idx] = byte(octet)
 322  				}
 323  				idx++
 324  				octet = 0
 325  			} else {
 326  				octet = octet*10 + int(host[i]-'0')
 327  			}
 328  		}
 329  		if idx < 4 {
 330  			ip[idx] = byte(octet)
 331  		}
 332  	}
 333  	return ip, port
 334  }
 335  
 336