racket/pkgs/net-test/tests/net/http-proxy/proxy-server.rkt
Sam Tobin-Hochstadt 4cc1503d15 Increase timeout.
2016-08-30 17:09:55 -04:00

110 lines
4.2 KiB
Racket
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#lang racket/base
;; A proxy HTTP server -- dont get your hopes up its for testing and
;; only proxies ports, probably oozes security leaks and I wouldnt be
;; surprised if it leaked fids too.
(require racket/port racket/match racket/tcp "generic-server.rkt")
(provide server
current-conn-timeout)
(define (http-tunnel-serve in out)
(let/ec
ec
(define-syntax-rule (out/flush fmt args ...)
(begin (fprintf out fmt args ...)
(flush-output out)))
(define request-lines (for/list ((l (in-lines in 'return-linefeed))
#:break (string=? l ""))
l))
;; frankly, I dont care about the headers... its just the request string
;; Im interested in
(match request-lines
[(cons (regexp #px"^(CONNECT)\\s+(\\S+):(\\d+)(\\s+HTTP/\\S+)?$"
(list _ method connect-host (app string->number connect-port) _)) _)
(define-values (connect:from connect:to)
(with-handlers ([exn:fail? (lambda (x)
;; any better ideas as to a good status code?
(out/flush "HTTP/1.1 410 Gone\r\n\r\n")
(ec))])
(tcp-connect connect-host connect-port)))
(file-stream-buffer-mode connect:to 'none)
(file-stream-buffer-mode connect:from 'none)
(out/flush "HTTP/1.1 200 Connection Established\r\n\r\n")
(define copy-in-to-connect:to-thread
(thread (lambda ()
(copy-port in connect:to)
(close-output-port connect:to))))
(define copy-connect:from-to-out-thread
(thread (lambda ()
(copy-port connect:from out)
(close-output-port out))))
(thread-wait copy-in-to-connect:to-thread)
(thread-wait copy-connect:from-to-out-thread)]
[(cons (regexp #px"^(\\S+)\\s+(\\S+)(\\s+HTTP/\\S+)?$"
(list request method request-uri http-version)) _)
(out/flush "HTTP/1.1 405 Method Not Allowed\r\n\r\n")]
[_ (out/flush "HTTP/1.1 400 Bad Request\r\n\r\n")])))
(define (server)
(serve http-tunnel-serve))
(module+
main
(define-values (the-port server-thread shutdown-server)
(server))
(thread-wait server-thread))
(module+
test
(module config info
(define timeout 300))
(require rackunit)
(require (prefix-in es: "echo-server.rkt"))
(define-values (proxy-listen-port server-thread shutdown-server)
(server))
(define-values (echo-port es:server-thread es:shutdown-server) (es:server))
(let ((old-exit-handler (exit-handler)))
(exit-handler (lambda (exit-code)
(shutdown-server)
(es:shutdown-server)
(old-exit-handler exit-code))))
(define (connect/test method uri http-version
#:headers (headers '())
#:body (body #f))
(define-values (cl:from cl:to) (tcp-connect "localhost" proxy-listen-port))
(file-stream-buffer-mode cl:from 'none)
(file-stream-buffer-mode cl:to 'none)
(if http-version
(fprintf cl:to "~a ~a ~a\r\n" method uri http-version)
(fprintf cl:to "~a ~a\r\n" method uri))
(for-each (lambda (h) (fprintf cl:to "~a\r\n" h)) headers)
(fprintf cl:to "\r\n") ; end headers
;; Not interested in any fancy interaction here... just see what the response is
(when body (write-string body cl:to))
(flush-output cl:to)
(close-output-port cl:to)
(begin0
(port->string cl:from)
(tcp-abandon-port cl:to)
(tcp-abandon-port cl:from)))
(check-match (connect/test "GET" "/" #f) (regexp #px"^HTTP/\\S+\\s+405"))
(check-match (connect/test "A B" "/" #f) (regexp #px"^HTTP/\\S+\\s+400"))
(check-match (connect/test "CONNECT" "q.com:9887" #f) (regexp #px"^HTTP/\\S+\\s+410"))
(check-match (connect/test "CONNECT" (format "localhost:~a" echo-port)
#f #:body "blah blah blah!")
(regexp #px"^HTTP/\\S+\\s+200.*blah!$"))
)