...

Source file src/github.com/docker/distribution/testutil/handler.go

Documentation: github.com/docker/distribution/testutil

     1  package testutil
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"net/url"
    10  	"sort"
    11  	"strings"
    12  )
    13  
    14  // RequestResponseMap is an ordered mapping from Requests to Responses
    15  type RequestResponseMap []RequestResponseMapping
    16  
    17  // RequestResponseMapping defines a Response to be sent in response to a given
    18  // Request
    19  type RequestResponseMapping struct {
    20  	Request  Request
    21  	Response Response
    22  }
    23  
    24  // Request is a simplified http.Request object
    25  type Request struct {
    26  	// Method is the http method of the request, for example GET
    27  	Method string
    28  
    29  	// Route is the http route of this request
    30  	Route string
    31  
    32  	// QueryParams are the query parameters of this request
    33  	QueryParams map[string][]string
    34  
    35  	// Body is the byte contents of the http request
    36  	Body []byte
    37  
    38  	// Headers are the header for this request
    39  	Headers http.Header
    40  }
    41  
    42  func (r Request) String() string {
    43  	queryString := ""
    44  	if len(r.QueryParams) > 0 {
    45  		keys := make([]string, 0, len(r.QueryParams))
    46  		queryParts := make([]string, 0, len(r.QueryParams))
    47  		for k := range r.QueryParams {
    48  			keys = append(keys, k)
    49  		}
    50  		sort.Strings(keys)
    51  		for _, k := range keys {
    52  			for _, val := range r.QueryParams[k] {
    53  				queryParts = append(queryParts, fmt.Sprintf("%s=%s", k, url.QueryEscape(val)))
    54  			}
    55  		}
    56  		queryString = "?" + strings.Join(queryParts, "&")
    57  	}
    58  	var headers []string
    59  	if len(r.Headers) > 0 {
    60  		var headerKeys []string
    61  		for k := range r.Headers {
    62  			headerKeys = append(headerKeys, k)
    63  		}
    64  		sort.Strings(headerKeys)
    65  
    66  		for _, k := range headerKeys {
    67  			for _, val := range r.Headers[k] {
    68  				headers = append(headers, fmt.Sprintf("%s:%s", k, val))
    69  			}
    70  		}
    71  
    72  	}
    73  	return fmt.Sprintf("%s %s%s\n%s\n%s", r.Method, r.Route, queryString, headers, r.Body)
    74  }
    75  
    76  // Response is a simplified http.Response object
    77  type Response struct {
    78  	// Statuscode is the http status code of the Response
    79  	StatusCode int
    80  
    81  	// Headers are the http headers of this Response
    82  	Headers http.Header
    83  
    84  	// Body is the response body
    85  	Body []byte
    86  }
    87  
    88  // testHandler is an http.Handler with a defined mapping from Request to an
    89  // ordered list of Response objects
    90  type testHandler struct {
    91  	responseMap map[string][]Response
    92  }
    93  
    94  // NewHandler returns a new test handler that responds to defined requests
    95  // with specified responses
    96  // Each time a Request is received, the next Response is returned in the
    97  // mapping, until no Responses are defined, at which point a 404 is sent back
    98  func NewHandler(requestResponseMap RequestResponseMap) http.Handler {
    99  	responseMap := make(map[string][]Response)
   100  	for _, mapping := range requestResponseMap {
   101  		responses, ok := responseMap[mapping.Request.String()]
   102  		if ok {
   103  			responseMap[mapping.Request.String()] = append(responses, mapping.Response)
   104  		} else {
   105  			responseMap[mapping.Request.String()] = []Response{mapping.Response}
   106  		}
   107  	}
   108  	return &testHandler{responseMap: responseMap}
   109  }
   110  
   111  func (app *testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   112  	defer r.Body.Close()
   113  
   114  	requestBody, _ := ioutil.ReadAll(r.Body)
   115  	request := Request{
   116  		Method:      r.Method,
   117  		Route:       r.URL.Path,
   118  		QueryParams: r.URL.Query(),
   119  		Body:        requestBody,
   120  		Headers:     make(map[string][]string),
   121  	}
   122  
   123  	// Add headers of interest here
   124  	for k, v := range r.Header {
   125  		if k == "If-None-Match" {
   126  			request.Headers[k] = v
   127  		}
   128  	}
   129  
   130  	responses, ok := app.responseMap[request.String()]
   131  
   132  	if !ok || len(responses) == 0 {
   133  		http.NotFound(w, r)
   134  		return
   135  	}
   136  
   137  	response := responses[0]
   138  	app.responseMap[request.String()] = responses[1:]
   139  
   140  	responseHeader := w.Header()
   141  	for k, v := range response.Headers {
   142  		responseHeader[k] = v
   143  	}
   144  
   145  	w.WriteHeader(response.StatusCode)
   146  
   147  	io.Copy(w, bytes.NewReader(response.Body))
   148  }
   149  

View as plain text